/*
 *
 *  sep_crypto.c - Crypto interface structures
 *
 *  Copyright(c) 2009-2011 Intel Corporation. All rights reserved.
 *  Contributions(c) 2009-2010 Discretix. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 *  more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 *  this program; if not, write to the Free Software Foundation, Inc., 59
 *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *  CONTACTS:
 *
 *  Mark Allyn		mark.a.allyn@intel.com
 *  Jayant Mangalampalli jayant.mangalampalli@intel.com
 *
 *  CHANGES:
 *
 *  2009.06.26	Initial publish
 *  2010.09.14  Upgrade to Medfield
 *  2011.02.22  Enable Kernel Crypto
 *
 */

/* #define DEBUG */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/crypto.h>
#include <crypto/internal/hash.h>
#include <crypto/scatterwalk.h>
#include <crypto/sha.h>
#include <crypto/md5.h>
#include <crypto/aes.h>
#include <crypto/des.h>
#include <crypto/hash.h>
#include "sep_driver_hw_defs.h"
#include "sep_driver_config.h"
#include "sep_driver_api.h"
#include "sep_dev.h"
#include "sep_crypto.h"

#if defined(CONFIG_CRYPTO) || defined(CONFIG_CRYPTO_MODULE)

/* Globals for queuing */
static spinlock_t queue_lock;
static struct crypto_queue sep_queue;

/* Declare of dequeuer */
static void sep_dequeuer(void *data);

/* TESTING */
/**
 *	sep_do_callback
 *	@work: pointer to work_struct
 *	This is what is called by the queue; it is generic so that it
 *	can be used by any type of operation as each different callback
 *	function can use the data parameter in its own way
 */
static void sep_do_callback(struct work_struct *work)
{
	struct sep_work_struct *sep_work = container_of(work,
		struct sep_work_struct, work);
	if (sep_work != NULL) {
		(sep_work->callback)(sep_work->data);
		kfree(sep_work);
	} else {
		pr_debug("sep crypto: do callback - NULL container\n");
	}
}

/**
 *	sep_submit_work
 *	@work_queue: pointer to struct_workqueue
 *	@funct: pointer to function to execute
 *	@data: pointer to data; function will know
 *		how to use it
 *	This is a generic API to submit something to
 *	the queue. The callback function will depend
 *	on what operation is to be done
 */
static int sep_submit_work(struct workqueue_struct *work_queue,
	void(*funct)(void *),
	void *data)
{
	struct sep_work_struct *sep_work;
	int result;

	sep_work = kmalloc(sizeof(struct sep_work_struct), GFP_ATOMIC);

	if (sep_work == NULL) {
		pr_debug("sep crypto: cant allocate work structure\n");
		return -ENOMEM;
	}

	sep_work->callback = funct;
	sep_work->data = data;
	INIT_WORK(&sep_work->work, sep_do_callback);
	result = queue_work(work_queue, &sep_work->work);
	if (!result) {
		pr_debug("sep_crypto: queue_work failed\n");
		return -EINVAL;
	}
	return 0;
}

/**
 *	sep_alloc_sg_buf -
 *	@sep: pointer to struct sep_device
 *	@size: total size of area
 *	@block_size: minimum size of chunks
 *	each page is minimum or modulo this size
 *	@returns: pointer to struct scatterlist for new
 *	buffer
 **/
static struct scatterlist *sep_alloc_sg_buf(
	struct sep_device *sep,
	size_t size,
	size_t block_size)
{
	u32 nbr_pages;
	u32 ct1;
	void *buf;
	size_t current_size;
	size_t real_page_size;

	struct scatterlist *sg, *sg_temp;

	if (size == 0)
		return NULL;

	dev_dbg(&sep->pdev->dev, "sep alloc sg buf\n");

	current_size = 0;
	nbr_pages = 0;
	real_page_size = PAGE_SIZE - (PAGE_SIZE % block_size);
	/**
	 * The size of each page must be modulo of the operation
	 * block size; increment by the modified page size until
	 * the total size is reached, then you have the number of
	 * pages
	 */
	while (current_size < size) {
		current_size += real_page_size;
		nbr_pages += 1;
	}

	sg = kmalloc_array(nbr_pages, sizeof(struct scatterlist), GFP_ATOMIC);
	if (!sg)
		return NULL;

	sg_init_table(sg, nbr_pages);

	current_size = 0;
	sg_temp = sg;
	for (ct1 = 0; ct1 < nbr_pages; ct1 += 1) {
		buf = (void *)get_zeroed_page(GFP_ATOMIC);
		if (!buf) {
			dev_warn(&sep->pdev->dev,
				"Cannot allocate page for new buffer\n");
			kfree(sg);
			return NULL;
		}

		sg_set_buf(sg_temp, buf, real_page_size);
		if ((size - current_size) > real_page_size) {
			sg_temp->length = real_page_size;
			current_size += real_page_size;
		} else {
			sg_temp->length = (size - current_size);
			current_size = size;
		}
		sg_temp = sg_next(sg);
	}
	return sg;
}

/**
 *	sep_free_sg_buf -
 *	@sg: pointer to struct scatterlist; points to area to free
 */
static void sep_free_sg_buf(struct scatterlist *sg)
{
	struct scatterlist *sg_temp = sg;
		while (sg_temp) {
			free_page((unsigned long)sg_virt(sg_temp));
			sg_temp = sg_next(sg_temp);
		}
		kfree(sg);
}

/**
 *	sep_copy_sg -
 *	@sep: pointer to struct sep_device
 *	@sg_src: pointer to struct scatterlist for source
 *	@sg_dst: pointer to struct scatterlist for destination
 *      @size: size (in bytes) of data to copy
 *
 *	Copy data from one scatterlist to another; both must
 *	be the same size
 */
static void sep_copy_sg(
	struct sep_device *sep,
	struct scatterlist *sg_src,
	struct scatterlist *sg_dst,
	size_t size)
{
	u32 seg_size;
	u32 in_offset, out_offset;

	u32 count = 0;
	struct scatterlist *sg_src_tmp = sg_src;
	struct scatterlist *sg_dst_tmp = sg_dst;
	in_offset = 0;
	out_offset = 0;

	dev_dbg(&sep->pdev->dev, "sep copy sg\n");

	if ((sg_src == NULL) || (sg_dst == NULL) || (size == 0))
		return;

	dev_dbg(&sep->pdev->dev, "sep copy sg not null\n");

	while (count < size) {
		if ((sg_src_tmp->length - in_offset) >
			(sg_dst_tmp->length - out_offset))
			seg_size = sg_dst_tmp->length - out_offset;
		else
			seg_size = sg_src_tmp->length - in_offset;

		if (seg_size > (size - count))
			seg_size = (size = count);

		memcpy(sg_virt(sg_dst_tmp) + out_offset,
			sg_virt(sg_src_tmp) + in_offset,
			seg_size);

		in_offset += seg_size;
		out_offset += seg_size;
		count += seg_size;

		if (in_offset >= sg_src_tmp->length) {
			sg_src_tmp = sg_next(sg_src_tmp);
			in_offset = 0;
		}

		if (out_offset >= sg_dst_tmp->length) {
			sg_dst_tmp = sg_next(sg_dst_tmp);
			out_offset = 0;
		}
	}
}

/**
 *	sep_oddball_pages -
 *	@sep: pointer to struct sep_device
 *	@sg: pointer to struct scatterlist - buffer to check
 *	@size: total data size
 *	@blocksize: minimum block size; must be multiples of this size
 *	@to_copy: 1 means do copy, 0 means do not copy
 *	@new_sg: pointer to location to put pointer to new sg area
 *	@returns: 1 if new scatterlist is needed; 0 if not needed;
 *		error value if operation failed
 *
 *	The SEP device requires all pages to be multiples of the
 *	minimum block size appropriate for the operation
 *	This function check all pages; if any are oddball sizes
 *	(not multiple of block sizes), it creates a new scatterlist.
 *	If the to_copy parameter is set to 1, then a scatter list
 *	copy is performed. The pointer to the new scatterlist is
 *	put into the address supplied by the new_sg parameter; if
 *	no new scatterlist is needed, then a NULL is put into
 *	the location at new_sg.
 *
 */
static int sep_oddball_pages(
	struct sep_device *sep,
	struct scatterlist *sg,
	size_t data_size,
	u32 block_size,
	struct scatterlist **new_sg,
	u32 do_copy)
{
	struct scatterlist *sg_temp;
	u32 flag;
	u32 nbr_pages, page_count;

	dev_dbg(&sep->pdev->dev, "sep oddball\n");
	if ((sg == NULL) || (data_size == 0) || (data_size < block_size))
		return 0;

	dev_dbg(&sep->pdev->dev, "sep oddball not null\n");
	flag = 0;
	nbr_pages = 0;
	page_count = 0;
	sg_temp = sg;

	while (sg_temp) {
		nbr_pages += 1;
		sg_temp = sg_next(sg_temp);
	}

	sg_temp = sg;
	while ((sg_temp) && (flag == 0)) {
		page_count += 1;
		if (sg_temp->length % block_size)
			flag = 1;
		else
			sg_temp = sg_next(sg_temp);
	}

	/* Do not process if last (or only) page is oddball */
	if (nbr_pages == page_count)
		flag = 0;

	if (flag) {
		dev_dbg(&sep->pdev->dev, "sep oddball processing\n");
		*new_sg = sep_alloc_sg_buf(sep, data_size, block_size);
		if (*new_sg == NULL) {
			dev_warn(&sep->pdev->dev, "cannot allocate new sg\n");
			return -ENOMEM;
		}

		if (do_copy)
			sep_copy_sg(sep, sg, *new_sg, data_size);

		return 1;
	} else {
		return 0;
	}
}

/**
 *	sep_copy_offset_sg -
 *	@sep: pointer to struct sep_device;
 *	@sg: pointer to struct scatterlist
 *	@offset: offset into scatterlist memory
 *	@dst: place to put data
 *	@len: length of data
 *	@returns: number of bytes copies
 *
 *	This copies data from scatterlist buffer
 *	offset from beginning - it is needed for
 *	handling tail data in hash
 */
static size_t sep_copy_offset_sg(
	struct sep_device *sep,
	struct scatterlist *sg,
	u32 offset,
	void *dst,
	u32 len)
{
	size_t page_start;
	size_t page_end;
	size_t offset_within_page;
	size_t length_within_page;
	size_t length_remaining;
	size_t current_offset;

	/* Find which page is beginning of segment */
	page_start = 0;
	page_end = sg->length;
	while ((sg) && (offset > page_end)) {
		page_start += sg->length;
		sg = sg_next(sg);
		if (sg)
			page_end += sg->length;
	}

	if (sg == NULL)
		return -ENOMEM;

	offset_within_page = offset - page_start;
	if ((sg->length - offset_within_page) >= len) {
		/* All within this page */
		memcpy(dst, sg_virt(sg) + offset_within_page, len);
		return len;
	} else {
		/* Scattered multiple pages */
		current_offset = 0;
		length_remaining = len;
		while ((sg) && (current_offset < len)) {
			length_within_page = sg->length - offset_within_page;
			if (length_within_page >= length_remaining) {
				memcpy(dst+current_offset,
					sg_virt(sg) + offset_within_page,
					length_remaining);
				length_remaining = 0;
				current_offset = len;
			} else {
				memcpy(dst+current_offset,
					sg_virt(sg) + offset_within_page,
					length_within_page);
				length_remaining -= length_within_page;
				current_offset += length_within_page;
				offset_within_page = 0;
				sg = sg_next(sg);
			}
		}

		if (sg == NULL)
			return -ENOMEM;
	}
	return len;
}

/**
 *	partial_overlap -
 *	@src_ptr: source pointer
 *	@dst_ptr: destination pointer
 *	@nbytes: number of bytes
 *	@returns: 0 for success; -1 for failure
 *	We cannot have any partial overlap. Total overlap
 *	where src is the same as dst is okay
 */
static int partial_overlap(void *src_ptr, void *dst_ptr, u32 nbytes)
{
	/* Check for partial overlap */
	if (src_ptr != dst_ptr) {
		if (src_ptr < dst_ptr) {
			if ((src_ptr + nbytes) > dst_ptr)
				return -EINVAL;
		} else {
			if ((dst_ptr + nbytes) > src_ptr)
				return -EINVAL;
		}
	}

	return 0;
}

/* Debug - prints only if DEBUG is defined */
static void sep_dump_ivs(struct ablkcipher_request *req, char *reason)

	{
	unsigned char *cptr;
	struct sep_aes_internal_context *aes_internal;
	struct sep_des_internal_context *des_internal;
	int ct1;

	struct this_task_ctx *ta_ctx;
	struct crypto_ablkcipher *tfm;
	struct sep_system_ctx *sctx;

	ta_ctx = ablkcipher_request_ctx(req);
	tfm = crypto_ablkcipher_reqtfm(req);
	sctx = crypto_ablkcipher_ctx(tfm);

	dev_dbg(&ta_ctx->sep_used->pdev->dev, "IV DUMP - %s\n", reason);
	if ((ta_ctx->current_request == DES_CBC) &&
		(ta_ctx->des_opmode == SEP_DES_CBC)) {

		des_internal = (struct sep_des_internal_context *)
			sctx->des_private_ctx.ctx_buf;
		/* print vendor */
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"sep - vendor iv for DES\n");
		cptr = (unsigned char *)des_internal->iv_context;
		for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1)
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"%02x\n", *(cptr + ct1));

		/* print walk */
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"sep - walk from kernel crypto iv for DES\n");
		cptr = (unsigned char *)ta_ctx->walk.iv;
		for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1)
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"%02x\n", *(cptr + ct1));
	} else if ((ta_ctx->current_request == AES_CBC) &&
		(ta_ctx->aes_opmode == SEP_AES_CBC)) {

		aes_internal = (struct sep_aes_internal_context *)
			sctx->aes_private_ctx.cbuff;
		/* print vendor */
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"sep - vendor iv for AES\n");
		cptr = (unsigned char *)aes_internal->aes_ctx_iv;
		for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1)
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"%02x\n", *(cptr + ct1));

		/* print walk */
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"sep - walk from kernel crypto iv for AES\n");
		cptr = (unsigned char *)ta_ctx->walk.iv;
		for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1)
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"%02x\n", *(cptr + ct1));
	}
}

/**
 * RFC2451: Weak key check
 * Returns: 1 (weak), 0 (not weak)
 */
static int sep_weak_key(const u8 *key, unsigned int keylen)
{
	static const u8 parity[] = {
	8, 1, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 2, 8,
	0, 8, 8, 0, 8, 0, 0, 8, 8,
	0, 0, 8, 0, 8, 8, 3,
	0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0,
	8, 0, 0, 8, 0, 8, 8, 0, 0,
	8, 8, 0, 8, 0, 0, 8,
	0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0,
	8, 0, 0, 8, 0, 8, 8, 0, 0,
	8, 8, 0, 8, 0, 0, 8,
	8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8,
	0, 8, 8, 0, 8, 0, 0, 8, 8,
	0, 0, 8, 0, 8, 8, 0,
	0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0,
	8, 0, 0, 8, 0, 8, 8, 0, 0,
	8, 8, 0, 8, 0, 0, 8,
	8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8,
	0, 8, 8, 0, 8, 0, 0, 8, 8,
	0, 0, 8, 0, 8, 8, 0,
	8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8,
	0, 8, 8, 0, 8, 0, 0, 8, 8,
	0, 0, 8, 0, 8, 8, 0,
	4, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0,
	8, 5, 0, 8, 0, 8, 8, 0, 0,
	8, 8, 0, 8, 0, 6, 8,
	};

	u32 n, w;

	n  = parity[key[0]]; n <<= 4;
	n |= parity[key[1]]; n <<= 4;
	n |= parity[key[2]]; n <<= 4;
	n |= parity[key[3]]; n <<= 4;
	n |= parity[key[4]]; n <<= 4;
	n |= parity[key[5]]; n <<= 4;
	n |= parity[key[6]]; n <<= 4;
	n |= parity[key[7]];
	w = 0x88888888L;

	/* 1 in 10^10 keys passes this test */
	if (!((n - (w >> 3)) & w)) {
		if (n < 0x41415151) {
			if (n < 0x31312121) {
				if (n < 0x14141515) {
					/* 01 01 01 01 01 01 01 01 */
					if (n == 0x11111111)
						goto weak;
					/* 01 1F 01 1F 01 0E 01 0E */
					if (n == 0x13131212)
						goto weak;
				} else {
					/* 01 E0 01 E0 01 F1 01 F1 */
					if (n == 0x14141515)
						goto weak;
					/* 01 FE 01 FE 01 FE 01 FE */
					if (n == 0x16161616)
						goto weak;
				}
			} else {
				if (n < 0x34342525) {
					/* 1F 01 1F 01 0E 01 0E 01 */
					if (n == 0x31312121)
						goto weak;
					/* 1F 1F 1F 1F 0E 0E 0E 0E (?) */
					if (n == 0x33332222)
						goto weak;
				} else {
					/* 1F E0 1F E0 0E F1 0E F1 */
					if (n == 0x34342525)
						goto weak;
					/* 1F FE 1F FE 0E FE 0E FE */
					if (n == 0x36362626)
						goto weak;
				}
			}
		} else {
			if (n < 0x61616161) {
				if (n < 0x44445555) {
					/* E0 01 E0 01 F1 01 F1 01 */
					if (n == 0x41415151)
						goto weak;
					/* E0 1F E0 1F F1 0E F1 0E */
					if (n == 0x43435252)
						goto weak;
				} else {
					/* E0 E0 E0 E0 F1 F1 F1 F1 (?) */
					if (n == 0x44445555)
						goto weak;
					/* E0 FE E0 FE F1 FE F1 FE */
					if (n == 0x46465656)
						goto weak;
				}
			} else {
				if (n < 0x64646565) {
					/* FE 01 FE 01 FE 01 FE 01 */
					if (n == 0x61616161)
						goto weak;
					/* FE 1F FE 1F FE 0E FE 0E */
					if (n == 0x63636262)
						goto weak;
				} else {
					/* FE E0 FE E0 FE F1 FE F1 */
					if (n == 0x64646565)
						goto weak;
					/* FE FE FE FE FE FE FE FE */
					if (n == 0x66666666)
						goto weak;
				}
			}
		}
	}
	return 0;
weak:
	return 1;
}
/**
 *	sep_sg_nents
 */
static u32 sep_sg_nents(struct scatterlist *sg)
{
	u32 ct1 = 0;
	while (sg) {
		ct1 += 1;
		sg = sg_next(sg);
	}

	return ct1;
}

/**
 *	sep_start_msg -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@returns: offset to place for the next word in the message
 *	Set up pointer in message pool for new message
 */
static u32 sep_start_msg(struct this_task_ctx *ta_ctx)
{
	u32 *word_ptr;
	ta_ctx->msg_len_words = 2;
	ta_ctx->msgptr = ta_ctx->msg;
	memset(ta_ctx->msg, 0, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);
	ta_ctx->msgptr += sizeof(u32) * 2;
	word_ptr = (u32 *)ta_ctx->msgptr;
	*word_ptr = SEP_START_MSG_TOKEN;
	return sizeof(u32) * 2;
}

/**
 *	sep_end_msg -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@messages_offset: current message offset
 *	Returns: 0 for success; <0 otherwise
 *	End message; set length and CRC; and
 *	send interrupt to the SEP
 */
static void sep_end_msg(struct this_task_ctx *ta_ctx, u32 msg_offset)
{
	u32 *word_ptr;
	/* Msg size goes into msg after token */
	ta_ctx->msg_len_words = msg_offset / sizeof(u32) + 1;
	word_ptr = (u32 *)ta_ctx->msgptr;
	word_ptr += 1;
	*word_ptr = ta_ctx->msg_len_words;

	/* CRC (currently 0) goes at end of msg */
	word_ptr = (u32 *)(ta_ctx->msgptr + msg_offset);
	*word_ptr = 0;
}

/**
 *	sep_start_inbound_msg -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@msg_offset: offset to place for the next word in the message
 *	@returns: 0 for success; error value for failure
 *	Set up pointer in message pool for inbound message
 */
static u32 sep_start_inbound_msg(struct this_task_ctx *ta_ctx, u32 *msg_offset)
{
	u32 *word_ptr;
	u32 token;
	u32 error = SEP_OK;

	*msg_offset = sizeof(u32) * 2;
	word_ptr = (u32 *)ta_ctx->msgptr;
	token = *word_ptr;
	ta_ctx->msg_len_words = *(word_ptr + 1);

	if (token != SEP_START_MSG_TOKEN) {
		error = SEP_INVALID_START;
		goto end_function;
	}

end_function:

	return error;
}

/**
 *	sep_write_msg -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@in_addr: pointer to start of parameter
 *	@size: size of parameter to copy (in bytes)
 *	@max_size: size to move up offset; SEP mesg is in word sizes
 *	@msg_offset: pointer to current offset (is updated)
 *	@byte_array: flag ti indicate whether endian must be changed
 *	Copies data into the message area from caller
 */
static void sep_write_msg(struct this_task_ctx *ta_ctx, void *in_addr,
	u32 size, u32 max_size, u32 *msg_offset, u32 byte_array)
{
	u32 *word_ptr;
	void *void_ptr;
	void_ptr = ta_ctx->msgptr + *msg_offset;
	word_ptr = (u32 *)void_ptr;
	memcpy(void_ptr, in_addr, size);
	*msg_offset += max_size;

	/* Do we need to manipulate endian? */
	if (byte_array) {
		u32 i;
		for (i = 0; i < ((size + 3) / 4); i += 1)
			*(word_ptr + i) = CHG_ENDIAN(*(word_ptr + i));
	}
}

/**
 *	sep_make_header
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@msg_offset: pointer to current offset (is updated)
 *	@op_code: op code to put into message
 *	Puts op code into message and updates offset
 */
static void sep_make_header(struct this_task_ctx *ta_ctx, u32 *msg_offset,
			    u32 op_code)
{
	u32 *word_ptr;

	*msg_offset = sep_start_msg(ta_ctx);
	word_ptr = (u32 *)(ta_ctx->msgptr + *msg_offset);
	*word_ptr = op_code;
	*msg_offset += sizeof(u32);
}



/**
 *	sep_read_msg -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@in_addr: pointer to start of parameter
 *	@size: size of parameter to copy (in bytes)
 *	@max_size: size to move up offset; SEP mesg is in word sizes
 *	@msg_offset: pointer to current offset (is updated)
 *	@byte_array: flag ti indicate whether endian must be changed
 *	Copies data out of the message area to caller
 */
static void sep_read_msg(struct this_task_ctx *ta_ctx, void *in_addr,
	u32 size, u32 max_size, u32 *msg_offset, u32 byte_array)
{
	u32 *word_ptr;
	void *void_ptr;
	void_ptr = ta_ctx->msgptr + *msg_offset;
	word_ptr = (u32 *)void_ptr;

	/* Do we need to manipulate endian? */
	if (byte_array) {
		u32 i;
		for (i = 0; i < ((size + 3) / 4); i += 1)
			*(word_ptr + i) = CHG_ENDIAN(*(word_ptr + i));
	}

	memcpy(in_addr, void_ptr, size);
	*msg_offset += max_size;
}

/**
 *	sep_verify_op -
 *	@ta_ctx: pointer to struct this_task_ctx
 *	@op_code: expected op_code
 *      @msg_offset: pointer to current offset (is updated)
 *	@returns: 0 for success; error for failure
 */
static u32 sep_verify_op(struct this_task_ctx *ta_ctx, u32 op_code,
			 u32 *msg_offset)
{
	u32 error;
	u32 in_ary[2];

	struct sep_device *sep = ta_ctx->sep_used;

	dev_dbg(&sep->pdev->dev, "dumping return message\n");
	error = sep_start_inbound_msg(ta_ctx, msg_offset);
	if (error) {
		dev_warn(&sep->pdev->dev,
			"sep_start_inbound_msg error\n");
		return error;
	}

	sep_read_msg(ta_ctx, in_ary, sizeof(u32) * 2, sizeof(u32) * 2,
		msg_offset, 0);

	if (in_ary[0] != op_code) {
		dev_warn(&sep->pdev->dev,
			"sep got back wrong opcode\n");
		dev_warn(&sep->pdev->dev,
			"got back %x; expected %x\n",
			in_ary[0], op_code);
		return SEP_WRONG_OPCODE;
	}

	if (in_ary[1] != SEP_OK) {
		dev_warn(&sep->pdev->dev,
			"sep execution error\n");
		dev_warn(&sep->pdev->dev,
			"got back %x; expected %x\n",
			in_ary[1], SEP_OK);
		return in_ary[0];
	}

return 0;
}

/**
 * sep_read_context -
 * @ta_ctx: pointer to struct this_task_ctx
 * @msg_offset: point to current place in SEP msg; is updated
 * @dst: pointer to place to put the context
 * @len: size of the context structure (differs for crypro/hash)
 * This function reads the context from the msg area
 * There is a special way the vendor needs to have the maximum
 * length calculated so that the msg_offset is updated properly;
 * it skips over some words in the msg area depending on the size
 * of the context
 */
static void sep_read_context(struct this_task_ctx *ta_ctx, u32 *msg_offset,
	void *dst, u32 len)
{
	u32 max_length = ((len + 3) / sizeof(u32)) * sizeof(u32);
	sep_read_msg(ta_ctx, dst, len, max_length, msg_offset, 0);
}

/**
 * sep_write_context -
 * @ta_ctx: pointer to struct this_task_ctx
 * @msg_offset: point to current place in SEP msg; is updated
 * @src: pointer to the current context
 * @len: size of the context structure (differs for crypro/hash)
 * This function writes the context to the msg area
 * There is a special way the vendor needs to have the maximum
 * length calculated so that the msg_offset is updated properly;
 * it skips over some words in the msg area depending on the size
 * of the context
 */
static void sep_write_context(struct this_task_ctx *ta_ctx, u32 *msg_offset,
	void *src, u32 len)
{
	u32 max_length = ((len + 3) / sizeof(u32)) * sizeof(u32);
	sep_write_msg(ta_ctx, src, len, max_length, msg_offset, 0);
}

/**
 * sep_clear_out -
 * @ta_ctx: pointer to struct this_task_ctx
 * Clear out crypto related values in sep device structure
 * to enable device to be used by anyone; either kernel
 * crypto or userspace app via middleware
 */
static void sep_clear_out(struct this_task_ctx *ta_ctx)
{
	if (ta_ctx->src_sg_hold) {
		sep_free_sg_buf(ta_ctx->src_sg_hold);
		ta_ctx->src_sg_hold = NULL;
	}

	if (ta_ctx->dst_sg_hold) {
		sep_free_sg_buf(ta_ctx->dst_sg_hold);
		ta_ctx->dst_sg_hold = NULL;
	}

	ta_ctx->src_sg = NULL;
	ta_ctx->dst_sg = NULL;

	sep_free_dma_table_data_handler(ta_ctx->sep_used, &ta_ctx->dma_ctx);

	if (ta_ctx->i_own_sep) {
		/**
		 * The following unlocks the sep and makes it available
		 * to any other application
		 * First, null out crypto entries in sep before releasing it
		 */
		ta_ctx->sep_used->current_hash_req = NULL;
		ta_ctx->sep_used->current_cypher_req = NULL;
		ta_ctx->sep_used->current_request = 0;
		ta_ctx->sep_used->current_hash_stage = 0;
		ta_ctx->sep_used->ta_ctx = NULL;
		ta_ctx->sep_used->in_kernel = 0;

		ta_ctx->call_status.status = 0;

		/* Remove anything confidential */
		memset(ta_ctx->sep_used->shared_addr, 0,
			SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

		sep_queue_status_remove(ta_ctx->sep_used, &ta_ctx->queue_elem);

#ifdef SEP_ENABLE_RUNTIME_PM
		ta_ctx->sep_used->in_use = 0;
		pm_runtime_mark_last_busy(&ta_ctx->sep_used->pdev->dev);
		pm_runtime_put_autosuspend(&ta_ctx->sep_used->pdev->dev);
#endif

		clear_bit(SEP_WORKING_LOCK_BIT,
			&ta_ctx->sep_used->in_use_flags);
		ta_ctx->sep_used->pid_doing_transaction = 0;

		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"[PID%d] waking up next transaction\n",
			current->pid);

		clear_bit(SEP_TRANSACTION_STARTED_LOCK_BIT,
			&ta_ctx->sep_used->in_use_flags);
		wake_up(&ta_ctx->sep_used->event_transactions);

		ta_ctx->i_own_sep = 0;
	}
}

/**
  * Release crypto infrastructure from EINPROGRESS and
  * clear sep_dev so that SEP is available to anyone
  */
static void sep_crypto_release(struct sep_system_ctx *sctx,
	struct this_task_ctx *ta_ctx, u32 error)
{
	struct ahash_request *hash_req = ta_ctx->current_hash_req;
	struct ablkcipher_request *cypher_req =
		ta_ctx->current_cypher_req;
	struct sep_device *sep = ta_ctx->sep_used;

	sep_clear_out(ta_ctx);

	/**
	 * This may not yet exist depending when we
	 * chose to bail out. If it does exist, set
	 * it to 1
	 */
	if (ta_ctx->are_we_done_yet != NULL)
		*ta_ctx->are_we_done_yet = 1;

	if (cypher_req != NULL) {
		if ((sctx->key_sent == 1) ||
			((error != 0) && (error != -EINPROGRESS))) {
			if (cypher_req->base.complete == NULL) {
				dev_dbg(&sep->pdev->dev,
					"release is null for cypher!");
			} else {
				cypher_req->base.complete(
					&cypher_req->base, error);
			}
		}
	}

	if (hash_req != NULL) {
		if (hash_req->base.complete == NULL) {
			dev_dbg(&sep->pdev->dev,
				"release is null for hash!");
		} else {
			hash_req->base.complete(
				&hash_req->base, error);
		}
	}
}

/**
 *	This is where we grab the sep itself and tell it to do something.
 *	It will sleep if the sep is currently busy
 *	and it will return 0 if sep is now ours; error value if there
 *	were problems
 */
static int sep_crypto_take_sep(struct this_task_ctx *ta_ctx)
{
	struct sep_device *sep = ta_ctx->sep_used;
	int result;
	struct sep_msgarea_hdr *my_msg_header;

	my_msg_header = (struct sep_msgarea_hdr *)ta_ctx->msg;

	/* add to status queue */
	ta_ctx->queue_elem = sep_queue_status_add(sep, my_msg_header->opcode,
		ta_ctx->nbytes, current->pid,
		current->comm, sizeof(current->comm));

	if (!ta_ctx->queue_elem) {
		dev_dbg(&sep->pdev->dev,
			"[PID%d] updating queue status error\n", current->pid);
		return -EINVAL;
	}

	/* get the device; this can sleep */
	result = sep_wait_transaction(sep);
	if (result)
		return result;

	if (sep_dev->power_save_setup == 1)
		pm_runtime_get_sync(&sep_dev->pdev->dev);

	/* Copy in the message */
	memcpy(sep->shared_addr, ta_ctx->msg,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	/* Copy in the dcb information if there is any */
	if (ta_ctx->dcb_region) {
		result = sep_activate_dcb_dmatables_context(sep,
			&ta_ctx->dcb_region, &ta_ctx->dmatables_region,
			ta_ctx->dma_ctx);
		if (result)
			return result;
	}

	/* Mark the device so we know how to finish the job in the tasklet */
	if (ta_ctx->current_hash_req)
		sep->current_hash_req = ta_ctx->current_hash_req;
	else
		sep->current_cypher_req = ta_ctx->current_cypher_req;

	sep->current_request = ta_ctx->current_request;
	sep->current_hash_stage = ta_ctx->current_hash_stage;
	sep->ta_ctx = ta_ctx;
	sep->in_kernel = 1;
	ta_ctx->i_own_sep = 1;

	/* need to set bit first to avoid race condition with interrupt */
	set_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET, &ta_ctx->call_status.status);

	result = sep_send_command_handler(sep);

	dev_dbg(&sep->pdev->dev, "[PID%d]: sending command to the sep\n",
		current->pid);

	if (!result)
		dev_dbg(&sep->pdev->dev, "[PID%d]: command sent okay\n",
			current->pid);
	else {
		dev_dbg(&sep->pdev->dev, "[PID%d]: cant send command\n",
			current->pid);
		clear_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET,
			&ta_ctx->call_status.status);
	}

	return result;
}

/**
 * This function sets things up for a crypto data block process
 * This does all preparation, but does not try to grab the
 * sep
 * @req: pointer to struct ablkcipher_request
 * returns: 0 if all went well, non zero if error
 */
static int sep_crypto_block_data(struct ablkcipher_request *req)
{

	int int_error;
	u32 msg_offset;
	static u32 msg[10];
	void *src_ptr;
	void *dst_ptr;

	static char small_buf[100];
	ssize_t copy_result;
	int result;

	struct scatterlist *new_sg;
	struct this_task_ctx *ta_ctx;
	struct crypto_ablkcipher *tfm;
	struct sep_system_ctx *sctx;

	struct sep_des_internal_context *des_internal;
	struct sep_aes_internal_context *aes_internal;

	ta_ctx = ablkcipher_request_ctx(req);
	tfm = crypto_ablkcipher_reqtfm(req);
	sctx = crypto_ablkcipher_ctx(tfm);

	/* start the walk on scatterlists */
	ablkcipher_walk_init(&ta_ctx->walk, req->src, req->dst, req->nbytes);
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep crypto block data size of %x\n",
		req->nbytes);

	int_error = ablkcipher_walk_phys(req, &ta_ctx->walk);
	if (int_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n",
			int_error);
		return -ENOMEM;
	}

	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"crypto block: src is %lx dst is %lx\n",
		(unsigned long)req->src, (unsigned long)req->dst);

	/* Make sure all pages are even block */
	int_error = sep_oddball_pages(ta_ctx->sep_used, req->src,
		req->nbytes, ta_ctx->walk.blocksize, &new_sg, 1);

	if (int_error < 0) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "oddball page error\n");
		return -ENOMEM;
	} else if (int_error == 1) {
		ta_ctx->src_sg = new_sg;
		ta_ctx->src_sg_hold = new_sg;
	} else {
		ta_ctx->src_sg = req->src;
		ta_ctx->src_sg_hold = NULL;
	}

	int_error = sep_oddball_pages(ta_ctx->sep_used, req->dst,
		req->nbytes, ta_ctx->walk.blocksize, &new_sg, 0);

	if (int_error < 0) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n",
			int_error);
		return -ENOMEM;
	} else if (int_error == 1) {
		ta_ctx->dst_sg = new_sg;
		ta_ctx->dst_sg_hold = new_sg;
	} else {
		ta_ctx->dst_sg = req->dst;
		ta_ctx->dst_sg_hold = NULL;
	}

	/* set nbytes for queue status */
	ta_ctx->nbytes = req->nbytes;

	/* Key already done; this is for data */
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "sending data\n");

	/* check for valid data and proper spacing */
	src_ptr = sg_virt(ta_ctx->src_sg);
	dst_ptr = sg_virt(ta_ctx->dst_sg);

	if (!src_ptr || !dst_ptr ||
		(ta_ctx->current_cypher_req->nbytes %
		crypto_ablkcipher_blocksize(tfm))) {

		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"cipher block size odd\n");
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"cipher block size is %x\n",
			crypto_ablkcipher_blocksize(tfm));
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"cipher data size is %x\n",
			ta_ctx->current_cypher_req->nbytes);
		return -EINVAL;
	}

	if (partial_overlap(src_ptr, dst_ptr,
		ta_ctx->current_cypher_req->nbytes)) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"block partial overlap\n");
		return -EINVAL;
	}

	/* Put together the message */
	sep_make_header(ta_ctx, &msg_offset, ta_ctx->block_opcode);

	/* If des, and size is 1 block, put directly in msg */
	if ((ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) &&
		(req->nbytes == crypto_ablkcipher_blocksize(tfm))) {

		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"writing out one block des\n");

		copy_result = sg_copy_to_buffer(
			ta_ctx->src_sg, sep_sg_nents(ta_ctx->src_sg),
			small_buf, crypto_ablkcipher_blocksize(tfm));

		if (copy_result != crypto_ablkcipher_blocksize(tfm)) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"des block copy failed\n");
			return -ENOMEM;
		}

		/* Put data into message */
		sep_write_msg(ta_ctx, small_buf,
			crypto_ablkcipher_blocksize(tfm),
			crypto_ablkcipher_blocksize(tfm) * 2,
			&msg_offset, 1);

		/* Put size into message */
		sep_write_msg(ta_ctx, &req->nbytes,
			sizeof(u32), sizeof(u32), &msg_offset, 0);
	} else {
		/* Otherwise, fill out dma tables */
		ta_ctx->dcb_input_data.app_in_address = src_ptr;
		ta_ctx->dcb_input_data.data_in_size = req->nbytes;
		ta_ctx->dcb_input_data.app_out_address = dst_ptr;
		ta_ctx->dcb_input_data.block_size =
			crypto_ablkcipher_blocksize(tfm);
		ta_ctx->dcb_input_data.tail_block_size = 0;
		ta_ctx->dcb_input_data.is_applet = 0;
		ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg;
		ta_ctx->dcb_input_data.dst_sg = ta_ctx->dst_sg;

		result = sep_create_dcb_dmatables_context_kernel(
			ta_ctx->sep_used,
			&ta_ctx->dcb_region,
			&ta_ctx->dmatables_region,
			&ta_ctx->dma_ctx,
			&ta_ctx->dcb_input_data,
			1);
		if (result) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"crypto dma table create failed\n");
			return -EINVAL;
		}

		/* Portion of msg is nulled (no data) */
		msg[0] = (u32)0;
		msg[1] = (u32)0;
		msg[2] = (u32)0;
		msg[3] = (u32)0;
		msg[4] = (u32)0;
		sep_write_msg(ta_ctx, (void *)msg, sizeof(u32) * 5,
			sizeof(u32) * 5, &msg_offset, 0);
		}

	/**
	 * Before we write the message, we need to overwrite the
	 * vendor's IV with the one from our own ablkcipher walk
	 * iv because this is needed for dm-crypt
	 */
	sep_dump_ivs(req, "sending data block to sep\n");
	if ((ta_ctx->current_request == DES_CBC) &&
		(ta_ctx->des_opmode == SEP_DES_CBC)) {

		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"overwrite vendor iv on DES\n");
		des_internal = (struct sep_des_internal_context *)
			sctx->des_private_ctx.ctx_buf;
		memcpy((void *)des_internal->iv_context,
			ta_ctx->walk.iv, crypto_ablkcipher_ivsize(tfm));
	} else if ((ta_ctx->current_request == AES_CBC) &&
		(ta_ctx->aes_opmode == SEP_AES_CBC)) {

		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"overwrite vendor iv on AES\n");
		aes_internal = (struct sep_aes_internal_context *)
			sctx->aes_private_ctx.cbuff;
		memcpy((void *)aes_internal->aes_ctx_iv,
			ta_ctx->walk.iv, crypto_ablkcipher_ivsize(tfm));
	}

	/* Write context into message */
	if (ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) {
		sep_write_context(ta_ctx, &msg_offset,
			&sctx->des_private_ctx,
			sizeof(struct sep_des_private_context));
	} else {
		sep_write_context(ta_ctx, &msg_offset,
			&sctx->aes_private_ctx,
			sizeof(struct sep_aes_private_context));
	}

	/* conclude message */
	sep_end_msg(ta_ctx, msg_offset);

	/* Parent (caller) is now ready to tell the sep to do ahead */
	return 0;
}


/**
 * This function sets things up for a crypto key submit process
 * This does all preparation, but does not try to grab the
 * sep
 * @req: pointer to struct ablkcipher_request
 * returns: 0 if all went well, non zero if error
 */
static int sep_crypto_send_key(struct ablkcipher_request *req)
{

	int int_error;
	u32 msg_offset;
	static u32 msg[10];

	u32 max_length;
	struct this_task_ctx *ta_ctx;
	struct crypto_ablkcipher *tfm;
	struct sep_system_ctx *sctx;

	ta_ctx = ablkcipher_request_ctx(req);
	tfm = crypto_ablkcipher_reqtfm(req);
	sctx = crypto_ablkcipher_ctx(tfm);

	dev_dbg(&ta_ctx->sep_used->pdev->dev, "sending key\n");

	/* start the walk on scatterlists */
	ablkcipher_walk_init(&ta_ctx->walk, req->src, req->dst, req->nbytes);
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"sep crypto block data size of %x\n", req->nbytes);

	int_error = ablkcipher_walk_phys(req, &ta_ctx->walk);
	if (int_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n",
			int_error);
		return -ENOMEM;
	}

	/* check iv */
	if ((ta_ctx->current_request == DES_CBC) &&
		(ta_ctx->des_opmode == SEP_DES_CBC)) {
		if (!ta_ctx->walk.iv) {
			dev_warn(&ta_ctx->sep_used->pdev->dev, "no iv found\n");
			return -EINVAL;
		}

		memcpy(ta_ctx->iv, ta_ctx->walk.iv, SEP_DES_IV_SIZE_BYTES);
	}

	if ((ta_ctx->current_request == AES_CBC) &&
		(ta_ctx->aes_opmode == SEP_AES_CBC)) {
		if (!ta_ctx->walk.iv) {
			dev_warn(&ta_ctx->sep_used->pdev->dev, "no iv found\n");
			return -EINVAL;
		}

		memcpy(ta_ctx->iv, ta_ctx->walk.iv, SEP_AES_IV_SIZE_BYTES);
	}

	/* put together message to SEP */
	/* Start with op code */
	sep_make_header(ta_ctx, &msg_offset, ta_ctx->init_opcode);

	/* now deal with IV */
	if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) {
		if (ta_ctx->des_opmode == SEP_DES_CBC) {
			sep_write_msg(ta_ctx, ta_ctx->iv,
				SEP_DES_IV_SIZE_BYTES, sizeof(u32) * 4,
				&msg_offset, 1);
		} else {
			/* Skip if ECB */
			msg_offset += 4 * sizeof(u32);
		}
	} else {
		max_length = ((SEP_AES_IV_SIZE_BYTES + 3) /
			sizeof(u32)) * sizeof(u32);
		if (ta_ctx->aes_opmode == SEP_AES_CBC) {
			sep_write_msg(ta_ctx, ta_ctx->iv,
				SEP_AES_IV_SIZE_BYTES, max_length,
				&msg_offset, 1);
		} else {
				/* Skip if ECB */
				msg_offset += max_length;
			}
		}

	/* load the key */
	if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) {
		sep_write_msg(ta_ctx, (void *)&sctx->key.des.key1,
			sizeof(u32) * 8, sizeof(u32) * 8,
			&msg_offset, 1);

		msg[0] = (u32)sctx->des_nbr_keys;
		msg[1] = (u32)ta_ctx->des_encmode;
		msg[2] = (u32)ta_ctx->des_opmode;

		sep_write_msg(ta_ctx, (void *)msg,
			sizeof(u32) * 3, sizeof(u32) * 3,
			&msg_offset, 0);
	} else {
		sep_write_msg(ta_ctx, (void *)&sctx->key.aes,
			sctx->keylen,
			SEP_AES_MAX_KEY_SIZE_BYTES,
			&msg_offset, 1);

		msg[0] = (u32)sctx->aes_key_size;
		msg[1] = (u32)ta_ctx->aes_encmode;
		msg[2] = (u32)ta_ctx->aes_opmode;
		msg[3] = (u32)0; /* Secret key is not used */
		sep_write_msg(ta_ctx, (void *)msg,
			sizeof(u32) * 4, sizeof(u32) * 4,
			&msg_offset, 0);
	}

	/* conclude message */
	sep_end_msg(ta_ctx, msg_offset);

	/* Parent (caller) is now ready to tell the sep to do ahead */
	return 0;
}


/* This needs to be run as a work queue as it can be put asleep */
static void sep_crypto_block(void *data)
{
	unsigned long end_time;

	int result;

	struct ablkcipher_request *req;
	struct this_task_ctx *ta_ctx;
	struct crypto_ablkcipher *tfm;
	struct sep_system_ctx *sctx;
	int are_we_done_yet;

	req = (struct ablkcipher_request *)data;
	ta_ctx = ablkcipher_request_ctx(req);
	tfm = crypto_ablkcipher_reqtfm(req);
	sctx = crypto_ablkcipher_ctx(tfm);

	ta_ctx->are_we_done_yet = &are_we_done_yet;

	pr_debug("sep_crypto_block\n");
	pr_debug("tfm is %p sctx is %p ta_ctx is %p\n",
		tfm, sctx, ta_ctx);
	pr_debug("key_sent is %d\n", sctx->key_sent);

	/* do we need to send the key */
	if (sctx->key_sent == 0) {
		are_we_done_yet = 0;
		result = sep_crypto_send_key(req); /* prep to send key */
		if (result != 0) {
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"could not prep key %x\n", result);
			sep_crypto_release(sctx, ta_ctx, result);
			return;
		}

		result = sep_crypto_take_sep(ta_ctx);
		if (result) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"sep_crypto_take_sep for key send failed\n");
			sep_crypto_release(sctx, ta_ctx, result);
			return;
		}

		/* now we sit and wait up to a fixed time for completion */
		end_time = jiffies + (WAIT_TIME * HZ);
		while ((time_before(jiffies, end_time)) &&
			(are_we_done_yet == 0))
			schedule();

		/* Done waiting; still not done yet? */
		if (are_we_done_yet == 0) {
			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"Send key job never got done\n");
			sep_crypto_release(sctx, ta_ctx, -EINVAL);
			return;
		}

		/* Set the key sent variable so this can be skipped later */
		sctx->key_sent = 1;
	}

	/* Key sent (or maybe not if we did not have to), now send block */
	are_we_done_yet = 0;

	result = sep_crypto_block_data(req);

	if (result != 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"could prep not send block %x\n", result);
		sep_crypto_release(sctx, ta_ctx, result);
		return;
	}

	result = sep_crypto_take_sep(ta_ctx);
	if (result) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"sep_crypto_take_sep for block send failed\n");
		sep_crypto_release(sctx, ta_ctx, result);
		return;
	}

	/* now we sit and wait up to a fixed time for completion */
	end_time = jiffies + (WAIT_TIME * HZ);
	while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0))
		schedule();

	/* Done waiting; still not done yet? */
	if (are_we_done_yet == 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"Send block job never got done\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

	/* That's it; entire thing done, get out of queue */

	pr_debug("crypto_block leaving\n");
	pr_debug("tfm is %p sctx is %p ta_ctx is %p\n", tfm, sctx, ta_ctx);
}

/**
 * Post operation (after interrupt) for crypto block
 */
static u32 crypto_post_op(struct sep_device *sep)
{
	/* HERE */
	u32 u32_error;
	u32 msg_offset;

	ssize_t copy_result;
	static char small_buf[100];

	struct ablkcipher_request *req;
	struct this_task_ctx *ta_ctx;
	struct sep_system_ctx *sctx;
	struct crypto_ablkcipher *tfm;

	struct sep_des_internal_context *des_internal;
	struct sep_aes_internal_context *aes_internal;

	if (!sep->current_cypher_req)
		return -EINVAL;

	/* hold req since we need to submit work after clearing sep */
	req = sep->current_cypher_req;

	ta_ctx = ablkcipher_request_ctx(sep->current_cypher_req);
	tfm = crypto_ablkcipher_reqtfm(sep->current_cypher_req);
	sctx = crypto_ablkcipher_ctx(tfm);

	pr_debug("crypto_post op\n");
	pr_debug("key_sent is %d tfm is %p sctx is %p ta_ctx is %p\n",
		sctx->key_sent, tfm, sctx, ta_ctx);

	dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto post_op\n");
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto post_op message dump\n");

	/* first bring msg from shared area to local area */
	memcpy(ta_ctx->msg, sep->shared_addr,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	/* Is this the result of performing init (key to SEP */
	if (sctx->key_sent == 0) {

		/* Did SEP do it okay */
		u32_error = sep_verify_op(ta_ctx, ta_ctx->init_opcode,
			&msg_offset);
		if (u32_error) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"aes init error %x\n", u32_error);
			sep_crypto_release(sctx, ta_ctx, u32_error);
			return u32_error;
			}

		/* Read Context */
		if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) {
			sep_read_context(ta_ctx, &msg_offset,
			&sctx->des_private_ctx,
			sizeof(struct sep_des_private_context));
		} else {
			sep_read_context(ta_ctx, &msg_offset,
			&sctx->aes_private_ctx,
			sizeof(struct sep_aes_private_context));
		}

		sep_dump_ivs(req, "after sending key to sep\n");

		/* key sent went okay; release sep, and set are_we_done_yet */
		sctx->key_sent = 1;
		sep_crypto_release(sctx, ta_ctx, -EINPROGRESS);

	} else {

		/**
		 * This is the result of a block request
		 */
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"crypto_post_op block response\n");

		u32_error = sep_verify_op(ta_ctx, ta_ctx->block_opcode,
			&msg_offset);

		if (u32_error) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"sep block error %x\n", u32_error);
			sep_crypto_release(sctx, ta_ctx, u32_error);
			return -EINVAL;
			}

		if (ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) {

			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"post op for DES\n");

			/* special case for 1 block des */
			if (sep->current_cypher_req->nbytes ==
				crypto_ablkcipher_blocksize(tfm)) {

				sep_read_msg(ta_ctx, small_buf,
					crypto_ablkcipher_blocksize(tfm),
					crypto_ablkcipher_blocksize(tfm) * 2,
					&msg_offset, 1);

				dev_dbg(&ta_ctx->sep_used->pdev->dev,
					"reading in block des\n");

				copy_result = sg_copy_from_buffer(
					ta_ctx->dst_sg,
					sep_sg_nents(ta_ctx->dst_sg),
					small_buf,
					crypto_ablkcipher_blocksize(tfm));

				if (copy_result !=
					crypto_ablkcipher_blocksize(tfm)) {

					dev_warn(&ta_ctx->sep_used->pdev->dev,
						"des block copy failed\n");
					sep_crypto_release(sctx, ta_ctx,
						-ENOMEM);
					return -ENOMEM;
				}
			}

			/* Read Context */
			sep_read_context(ta_ctx, &msg_offset,
				&sctx->des_private_ctx,
				sizeof(struct sep_des_private_context));
		} else {

			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"post op for AES\n");

			/* Skip the MAC Output */
			msg_offset += (sizeof(u32) * 4);

			/* Read Context */
			sep_read_context(ta_ctx, &msg_offset,
				&sctx->aes_private_ctx,
				sizeof(struct sep_aes_private_context));
		}

		/* Copy to correct sg if this block had oddball pages */
		if (ta_ctx->dst_sg_hold)
			sep_copy_sg(ta_ctx->sep_used,
				ta_ctx->dst_sg,
				ta_ctx->current_cypher_req->dst,
				ta_ctx->current_cypher_req->nbytes);

		/**
		 * Copy the iv's back to the walk.iv
		 * This is required for dm_crypt
		 */
		sep_dump_ivs(req, "got data block from sep\n");
		if ((ta_ctx->current_request == DES_CBC) &&
			(ta_ctx->des_opmode == SEP_DES_CBC)) {

			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"returning result iv to walk on DES\n");
			des_internal = (struct sep_des_internal_context *)
				sctx->des_private_ctx.ctx_buf;
			memcpy(ta_ctx->walk.iv,
				(void *)des_internal->iv_context,
				crypto_ablkcipher_ivsize(tfm));
		} else if ((ta_ctx->current_request == AES_CBC) &&
			(ta_ctx->aes_opmode == SEP_AES_CBC)) {

			dev_dbg(&ta_ctx->sep_used->pdev->dev,
				"returning result iv to walk on AES\n");
			aes_internal = (struct sep_aes_internal_context *)
				sctx->aes_private_ctx.cbuff;
			memcpy(ta_ctx->walk.iv,
				(void *)aes_internal->aes_ctx_iv,
				crypto_ablkcipher_ivsize(tfm));
		}

		/* finished, release everything */
		sep_crypto_release(sctx, ta_ctx, 0);
	}
	pr_debug("crypto_post_op done\n");
	pr_debug("key_sent is %d tfm is %p sctx is %p ta_ctx is %p\n",
		sctx->key_sent, tfm, sctx, ta_ctx);

	return 0;
}

static u32 hash_init_post_op(struct sep_device *sep)
{
	u32 u32_error;
	u32 msg_offset;
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req);
	struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req);
	struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm);
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"hash init post op\n");

	/* first bring msg from shared area to local area */
	memcpy(ta_ctx->msg, sep->shared_addr,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	u32_error = sep_verify_op(ta_ctx, SEP_HASH_INIT_OPCODE,
		&msg_offset);

	if (u32_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "hash init error %x\n",
			u32_error);
		sep_crypto_release(sctx, ta_ctx, u32_error);
		return u32_error;
		}

	/* Read Context */
	sep_read_context(ta_ctx, &msg_offset,
		&sctx->hash_private_ctx,
		sizeof(struct sep_hash_private_context));

	/* Signal to crypto infrastructure and clear out */
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash init post op done\n");
	sep_crypto_release(sctx, ta_ctx, 0);
	return 0;
}

static u32 hash_update_post_op(struct sep_device *sep)
{
	u32 u32_error;
	u32 msg_offset;
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req);
	struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req);
	struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm);
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"hash update post op\n");

	/* first bring msg from shared area to local area */
	memcpy(ta_ctx->msg, sep->shared_addr,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	u32_error = sep_verify_op(ta_ctx, SEP_HASH_UPDATE_OPCODE,
		&msg_offset);

	if (u32_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "hash init error %x\n",
			u32_error);
		sep_crypto_release(sctx, ta_ctx, u32_error);
		return u32_error;
		}

	/* Read Context */
	sep_read_context(ta_ctx, &msg_offset,
		&sctx->hash_private_ctx,
		sizeof(struct sep_hash_private_context));

	/**
	 * Following is only for finup; if we just completed the
	 * data portion of finup, we now need to kick off the
	 * finish portion of finup.
	 */

	if (ta_ctx->sep_used->current_hash_stage == HASH_FINUP_DATA) {

		/* first reset stage to HASH_FINUP_FINISH */
		ta_ctx->sep_used->current_hash_stage = HASH_FINUP_FINISH;

		/* now enqueue the finish operation */
		spin_lock_irq(&queue_lock);
		u32_error = crypto_enqueue_request(&sep_queue,
			&ta_ctx->sep_used->current_hash_req->base);
		spin_unlock_irq(&queue_lock);

		if ((u32_error != 0) && (u32_error != -EINPROGRESS)) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"spe cypher post op cant queue\n");
			sep_crypto_release(sctx, ta_ctx, u32_error);
			return u32_error;
		}

		/* schedule the data send */
		u32_error = sep_submit_work(ta_ctx->sep_used->workqueue,
			sep_dequeuer, (void *)&sep_queue);

		if (u32_error) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"cant submit work sep_crypto_block\n");
			sep_crypto_release(sctx, ta_ctx, -EINVAL);
			return -EINVAL;
		}
	}

	/* Signal to crypto infrastructure and clear out */
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash update post op done\n");
	sep_crypto_release(sctx, ta_ctx, 0);
	return 0;
}

static u32 hash_final_post_op(struct sep_device *sep)
{
	int max_length;
	u32 u32_error;
	u32 msg_offset;
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req);
	struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm);
	struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req);
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"hash final post op\n");

	/* first bring msg from shared area to local area */
	memcpy(ta_ctx->msg, sep->shared_addr,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	u32_error = sep_verify_op(ta_ctx, SEP_HASH_FINISH_OPCODE,
		&msg_offset);

	if (u32_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev, "hash finish error %x\n",
			u32_error);
		sep_crypto_release(sctx, ta_ctx, u32_error);
		return u32_error;
		}

	/* Grab the result */
	if (ta_ctx->current_hash_req->result == NULL) {
		/* Oops, null buffer; error out here */
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"hash finish null buffer\n");
		sep_crypto_release(sctx, ta_ctx, (u32)-ENOMEM);
		return -ENOMEM;
		}

	max_length = (((SEP_HASH_RESULT_SIZE_WORDS * sizeof(u32)) + 3) /
		sizeof(u32)) * sizeof(u32);

	sep_read_msg(ta_ctx,
		ta_ctx->current_hash_req->result,
		crypto_ahash_digestsize(tfm), max_length,
		&msg_offset, 0);

	/* Signal to crypto infrastructure and clear out */
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash finish post op done\n");
	sep_crypto_release(sctx, ta_ctx, 0);
	return 0;
}

static u32 hash_digest_post_op(struct sep_device *sep)
{
	int max_length;
	u32 u32_error;
	u32 msg_offset;
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req);
	struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm);
	struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req);
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"hash digest post op\n");

	/* first bring msg from shared area to local area */
	memcpy(ta_ctx->msg, sep->shared_addr,
		SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES);

	u32_error = sep_verify_op(ta_ctx, SEP_HASH_SINGLE_OPCODE,
		&msg_offset);

	if (u32_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"hash digest finish error %x\n", u32_error);

		sep_crypto_release(sctx, ta_ctx, u32_error);
		return u32_error;
		}

	/* Grab the result */
	if (ta_ctx->current_hash_req->result == NULL) {
		/* Oops, null buffer; error out here */
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"hash digest finish null buffer\n");
		sep_crypto_release(sctx, ta_ctx, (u32)-ENOMEM);
		return -ENOMEM;
		}

	max_length = (((SEP_HASH_RESULT_SIZE_WORDS * sizeof(u32)) + 3) /
		sizeof(u32)) * sizeof(u32);

	sep_read_msg(ta_ctx,
		ta_ctx->current_hash_req->result,
		crypto_ahash_digestsize(tfm), max_length,
		&msg_offset, 0);

	/* Signal to crypto infrastructure and clear out */
	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"hash digest finish post op done\n");

	sep_crypto_release(sctx, ta_ctx, 0);
	return 0;
}

/**
 * The sep_finish function is the function that is scheduled (via tasklet)
 * by the interrupt service routine when the SEP sends and interrupt
 * This is only called by the interrupt handler as a tasklet.
 */
static void sep_finish(unsigned long data)
{
	struct sep_device *sep_dev;
	int res;

	res = 0;

	if (data == 0) {
		pr_debug("sep_finish called with null data\n");
		return;
	}

	sep_dev = (struct sep_device *)data;
	if (sep_dev == NULL) {
		pr_debug("sep_finish; sep_dev is NULL\n");
		return;
	}

	if (sep_dev->in_kernel == (u32)0) {
		dev_warn(&sep_dev->pdev->dev,
			"sep_finish; not in kernel operation\n");
		return;
	}

	/* Did we really do a sep command prior to this? */
	if (0 == test_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET,
		&sep_dev->ta_ctx->call_status.status)) {

		dev_warn(&sep_dev->pdev->dev, "[PID%d] sendmsg not called\n",
			current->pid);
		return;
	}

	if (sep_dev->send_ct != sep_dev->reply_ct) {
		dev_warn(&sep_dev->pdev->dev,
			"[PID%d] poll; no message came back\n",
			current->pid);
		return;
	}

	/* Check for error (In case time ran out) */
	if ((res != 0x0) && (res != 0x8)) {
		dev_warn(&sep_dev->pdev->dev,
			"[PID%d] poll; poll error GPR3 is %x\n",
			current->pid, res);
		return;
	}

	/* What kind of interrupt from sep was this? */
	res = sep_read_reg(sep_dev, HW_HOST_SEP_HOST_GPR2_REG_ADDR);

	dev_dbg(&sep_dev->pdev->dev, "[PID%d] GPR2 at crypto finish is %x\n",
		current->pid, res);

	/* Print request? */
	if ((res >> 30) & 0x1) {
		dev_dbg(&sep_dev->pdev->dev, "[PID%d] sep print req\n",
			current->pid);
		dev_dbg(&sep_dev->pdev->dev, "[PID%d] contents: %s\n",
			current->pid,
			(char *)(sep_dev->shared_addr +
			SEP_DRIVER_PRINTF_OFFSET_IN_BYTES));
		return;
	}

	/* Request for daemon (not currently in POR)? */
	if (res >> 31) {
		dev_dbg(&sep_dev->pdev->dev,
			"[PID%d] sep request; ignoring\n",
			current->pid);
		return;
	}

	/* If we got here, then we have a replay to a sep command */

	dev_dbg(&sep_dev->pdev->dev,
		"[PID%d] sep reply to command; processing request: %x\n",
		current->pid, sep_dev->current_request);

	switch (sep_dev->current_request) {
	case AES_CBC:
	case AES_ECB:
	case DES_CBC:
	case DES_ECB:
		res = crypto_post_op(sep_dev);
		break;
	case SHA1:
	case MD5:
	case SHA224:
	case SHA256:
		switch (sep_dev->current_hash_stage) {
		case HASH_INIT:
			res = hash_init_post_op(sep_dev);
			break;
		case HASH_UPDATE:
		case HASH_FINUP_DATA:
			res = hash_update_post_op(sep_dev);
			break;
		case HASH_FINUP_FINISH:
		case HASH_FINISH:
			res = hash_final_post_op(sep_dev);
			break;
		case HASH_DIGEST:
			res = hash_digest_post_op(sep_dev);
			break;
		default:
			pr_debug("sep - invalid stage for hash finish\n");
		}
		break;
	default:
		pr_debug("sep - invalid request for finish\n");
	}

	if (res)
		pr_debug("sep - finish returned error %x\n", res);
}

static int sep_hash_cra_init(struct crypto_tfm *tfm)
	{
	const char *alg_name = crypto_tfm_alg_name(tfm);

	pr_debug("sep_hash_cra_init name is %s\n", alg_name);

	crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
		sizeof(struct this_task_ctx));
	return 0;
	}

static void sep_hash_cra_exit(struct crypto_tfm *tfm)
{
	pr_debug("sep_hash_cra_exit\n");
}

static void sep_hash_init(void *data)
{
	u32 msg_offset;
	int result;
	struct ahash_request *req;
	struct crypto_ahash *tfm;
	struct this_task_ctx *ta_ctx;
	struct sep_system_ctx *sctx;
	unsigned long end_time;
	int are_we_done_yet;

	req = (struct ahash_request *)data;
	tfm = crypto_ahash_reqtfm(req);
	sctx = crypto_ahash_ctx(tfm);
	ta_ctx = ahash_request_ctx(req);
	ta_ctx->sep_used = sep_dev;

	ta_ctx->are_we_done_yet = &are_we_done_yet;

	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"sep_hash_init\n");
	ta_ctx->current_hash_stage = HASH_INIT;
	/* opcode and mode */
	sep_make_header(ta_ctx, &msg_offset, SEP_HASH_INIT_OPCODE);
	sep_write_msg(ta_ctx, &ta_ctx->hash_opmode,
		sizeof(u32), sizeof(u32), &msg_offset, 0);
	sep_end_msg(ta_ctx, msg_offset);

	are_we_done_yet = 0;
	result = sep_crypto_take_sep(ta_ctx);
	if (result) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"sep_hash_init take sep failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
	}

	/* now we sit and wait up to a fixed time for completion */
	end_time = jiffies + (WAIT_TIME * HZ);
	while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0))
		schedule();

	/* Done waiting; still not done yet? */
	if (are_we_done_yet == 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"hash init never got done\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

}

static void sep_hash_update(void *data)
{
	int int_error;
	u32 msg_offset;
	u32 len;
	struct sep_hash_internal_context *int_ctx;
	u32 block_size;
	u32 head_len;
	u32 tail_len;
	int are_we_done_yet;

	static u32 msg[10];
	static char small_buf[100];
	void *src_ptr;
	struct scatterlist *new_sg;
	ssize_t copy_result;
	struct ahash_request *req;
	struct crypto_ahash *tfm;
	struct this_task_ctx *ta_ctx;
	struct sep_system_ctx *sctx;
	unsigned long end_time;

	req = (struct ahash_request *)data;
	tfm = crypto_ahash_reqtfm(req);
	sctx = crypto_ahash_ctx(tfm);
	ta_ctx = ahash_request_ctx(req);
	ta_ctx->sep_used = sep_dev;

	ta_ctx->are_we_done_yet = &are_we_done_yet;

	/* length for queue status */
	ta_ctx->nbytes = req->nbytes;

	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"sep_hash_update\n");
	ta_ctx->current_hash_stage = HASH_UPDATE;
	len = req->nbytes;

	block_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
	tail_len = req->nbytes % block_size;
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "length is %x\n", len);
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "block_size is %x\n", block_size);
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "tail len is %x\n", tail_len);

	/* Compute header/tail sizes */
	int_ctx = (struct sep_hash_internal_context *)&sctx->
		hash_private_ctx.internal_context;
	head_len = (block_size - int_ctx->prev_update_bytes) % block_size;
	tail_len = (req->nbytes - head_len) % block_size;

	/* Make sure all pages are an even block */
	int_error = sep_oddball_pages(ta_ctx->sep_used, req->src,
		req->nbytes,
		block_size, &new_sg, 1);

	if (int_error < 0) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"oddball pages error in crash update\n");
		sep_crypto_release(sctx, ta_ctx, -ENOMEM);
		return;
	} else if (int_error == 1) {
		ta_ctx->src_sg = new_sg;
		ta_ctx->src_sg_hold = new_sg;
	} else {
		ta_ctx->src_sg = req->src;
		ta_ctx->src_sg_hold = NULL;
	}

	src_ptr = sg_virt(ta_ctx->src_sg);

	if ((!req->nbytes) || (!ta_ctx->src_sg)) {
		/* null data */
		src_ptr = NULL;
	}

	ta_ctx->dcb_input_data.app_in_address = src_ptr;
	ta_ctx->dcb_input_data.data_in_size =
		req->nbytes - (head_len + tail_len);
	ta_ctx->dcb_input_data.app_out_address = NULL;
	ta_ctx->dcb_input_data.block_size = block_size;
	ta_ctx->dcb_input_data.tail_block_size = 0;
	ta_ctx->dcb_input_data.is_applet = 0;
	ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg;
	ta_ctx->dcb_input_data.dst_sg = NULL;

	int_error = sep_create_dcb_dmatables_context_kernel(
		ta_ctx->sep_used,
		&ta_ctx->dcb_region,
		&ta_ctx->dmatables_region,
		&ta_ctx->dma_ctx,
		&ta_ctx->dcb_input_data,
		1);
	if (int_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"hash update dma table create failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

	/* Construct message to SEP */
	sep_make_header(ta_ctx, &msg_offset, SEP_HASH_UPDATE_OPCODE);

	msg[0] = (u32)0;
	msg[1] = (u32)0;
	msg[2] = (u32)0;

	sep_write_msg(ta_ctx, msg, sizeof(u32) * 3, sizeof(u32) * 3,
		&msg_offset, 0);

	/* Handle remainders */

	/* Head */
	sep_write_msg(ta_ctx, &head_len, sizeof(u32),
		sizeof(u32), &msg_offset, 0);

	if (head_len) {
		copy_result = sg_copy_to_buffer(
			req->src,
			sep_sg_nents(ta_ctx->src_sg),
			small_buf, head_len);

		if (copy_result != head_len) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"sg head copy failure in hash block\n");
			sep_crypto_release(sctx, ta_ctx, -ENOMEM);
			return;
		}

		sep_write_msg(ta_ctx, small_buf, head_len,
			sizeof(u32) * 32, &msg_offset, 1);
	} else {
		msg_offset += sizeof(u32) * 32;
	}

	/* Tail */
	sep_write_msg(ta_ctx, &tail_len, sizeof(u32),
		sizeof(u32), &msg_offset, 0);

	if (tail_len) {
		copy_result = sep_copy_offset_sg(
			ta_ctx->sep_used,
			ta_ctx->src_sg,
			req->nbytes - tail_len,
			small_buf, tail_len);

		if (copy_result != tail_len) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"sg tail copy failure in hash block\n");
			sep_crypto_release(sctx, ta_ctx, -ENOMEM);
			return;
		}

		sep_write_msg(ta_ctx, small_buf, tail_len,
			sizeof(u32) * 32, &msg_offset, 1);
	} else {
		msg_offset += sizeof(u32) * 32;
	}

	/* Context */
	sep_write_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx,
		sizeof(struct sep_hash_private_context));

	sep_end_msg(ta_ctx, msg_offset);
	are_we_done_yet = 0;
	int_error = sep_crypto_take_sep(ta_ctx);
	if (int_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"sep_hash_update take sep failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
	}

	/* now we sit and wait up to a fixed time for completion */
	end_time = jiffies + (WAIT_TIME * HZ);
	while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0))
		schedule();

	/* Done waiting; still not done yet? */
	if (are_we_done_yet == 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"hash update never got done\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

}

static void sep_hash_final(void *data)
{
	u32 msg_offset;
	struct ahash_request *req;
	struct crypto_ahash *tfm;
	struct this_task_ctx *ta_ctx;
	struct sep_system_ctx *sctx;
	int result;
	unsigned long end_time;
	int are_we_done_yet;

	req = (struct ahash_request *)data;
	tfm = crypto_ahash_reqtfm(req);
	sctx = crypto_ahash_ctx(tfm);
	ta_ctx = ahash_request_ctx(req);
	ta_ctx->sep_used = sep_dev;

	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"sep_hash_final\n");
	ta_ctx->current_hash_stage = HASH_FINISH;

	ta_ctx->are_we_done_yet = &are_we_done_yet;

	/* opcode and mode */
	sep_make_header(ta_ctx, &msg_offset, SEP_HASH_FINISH_OPCODE);

	/* Context */
	sep_write_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx,
		sizeof(struct sep_hash_private_context));

	sep_end_msg(ta_ctx, msg_offset);
	are_we_done_yet = 0;
	result = sep_crypto_take_sep(ta_ctx);
	if (result) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"sep_hash_final take sep failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
	}

	/* now we sit and wait up to a fixed time for completion */
	end_time = jiffies + (WAIT_TIME * HZ);
	while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0))
		schedule();

	/* Done waiting; still not done yet? */
	if (are_we_done_yet == 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"hash final job never got done\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

}

static void sep_hash_digest(void *data)
{
	int int_error;
	u32 msg_offset;
	u32 block_size;
	u32 msg[10];
	size_t copy_result;
	int result;
	int are_we_done_yet;
	u32 tail_len;
	static char small_buf[100];
	struct scatterlist *new_sg;
	void *src_ptr;

	struct ahash_request *req;
	struct crypto_ahash *tfm;
	struct this_task_ctx *ta_ctx;
	struct sep_system_ctx *sctx;
	unsigned long end_time;

	req = (struct ahash_request *)data;
	tfm = crypto_ahash_reqtfm(req);
	sctx = crypto_ahash_ctx(tfm);
	ta_ctx = ahash_request_ctx(req);
	ta_ctx->sep_used = sep_dev;

	dev_dbg(&ta_ctx->sep_used->pdev->dev,
		"sep_hash_digest\n");
	ta_ctx->current_hash_stage = HASH_DIGEST;

	ta_ctx->are_we_done_yet = &are_we_done_yet;

	/* length for queue status */
	ta_ctx->nbytes = req->nbytes;

	block_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
	tail_len = req->nbytes % block_size;
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "length is %x\n", req->nbytes);
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "block_size is %x\n", block_size);
	dev_dbg(&ta_ctx->sep_used->pdev->dev, "tail len is %x\n", tail_len);

	/* Make sure all pages are an even block */
	int_error = sep_oddball_pages(ta_ctx->sep_used, req->src,
		req->nbytes,
		block_size, &new_sg, 1);

	if (int_error < 0) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"oddball pages error in crash update\n");
		sep_crypto_release(sctx, ta_ctx, -ENOMEM);
		return;
	} else if (int_error == 1) {
		ta_ctx->src_sg = new_sg;
		ta_ctx->src_sg_hold = new_sg;
	} else {
		ta_ctx->src_sg = req->src;
		ta_ctx->src_sg_hold = NULL;
	}

	src_ptr = sg_virt(ta_ctx->src_sg);

	if ((!req->nbytes) || (!ta_ctx->src_sg)) {
		/* null data */
		src_ptr = NULL;
	}

	ta_ctx->dcb_input_data.app_in_address = src_ptr;
	ta_ctx->dcb_input_data.data_in_size = req->nbytes - tail_len;
	ta_ctx->dcb_input_data.app_out_address = NULL;
	ta_ctx->dcb_input_data.block_size = block_size;
	ta_ctx->dcb_input_data.tail_block_size = 0;
	ta_ctx->dcb_input_data.is_applet = 0;
	ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg;
	ta_ctx->dcb_input_data.dst_sg = NULL;

	int_error = sep_create_dcb_dmatables_context_kernel(
		ta_ctx->sep_used,
		&ta_ctx->dcb_region,
		&ta_ctx->dmatables_region,
		&ta_ctx->dma_ctx,
		&ta_ctx->dcb_input_data,
		1);
	if (int_error) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"hash update dma table create failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

	/* Construct message to SEP */
	sep_make_header(ta_ctx, &msg_offset, SEP_HASH_SINGLE_OPCODE);
	sep_write_msg(ta_ctx, &ta_ctx->hash_opmode,
		sizeof(u32), sizeof(u32), &msg_offset, 0);

	msg[0] = (u32)0;
	msg[1] = (u32)0;
	msg[2] = (u32)0;

	sep_write_msg(ta_ctx, msg, sizeof(u32) * 3, sizeof(u32) * 3,
		&msg_offset, 0);

	/* Tail */
	sep_write_msg(ta_ctx, &tail_len, sizeof(u32),
		sizeof(u32), &msg_offset, 0);

	if (tail_len) {
		copy_result = sep_copy_offset_sg(
			ta_ctx->sep_used,
			ta_ctx->src_sg,
			req->nbytes - tail_len,
			small_buf, tail_len);

		if (copy_result != tail_len) {
			dev_warn(&ta_ctx->sep_used->pdev->dev,
				"sg tail copy failure in hash block\n");
			sep_crypto_release(sctx, ta_ctx, -ENOMEM);
			return;
		}

		sep_write_msg(ta_ctx, small_buf, tail_len,
			sizeof(u32) * 32, &msg_offset, 1);
	} else {
		msg_offset += sizeof(u32) * 32;
	}

	sep_end_msg(ta_ctx, msg_offset);

	are_we_done_yet = 0;
	result = sep_crypto_take_sep(ta_ctx);
	if (result) {
		dev_warn(&ta_ctx->sep_used->pdev->dev,
			"sep_hash_digest take sep failed\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
	}

	/* now we sit and wait up to a fixed time for completion */
	end_time = jiffies + (WAIT_TIME * HZ);
	while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0))
		schedule();

	/* Done waiting; still not done yet? */
	if (are_we_done_yet == 0) {
		dev_dbg(&ta_ctx->sep_used->pdev->dev,
			"hash digest job never got done\n");
		sep_crypto_release(sctx, ta_ctx, -EINVAL);
		return;
	}

}

/**
 * This is what is called by each of the API's provided
 * in the kernel crypto descriptors. It is run in a process
 * context using the kernel workqueues. Therefore it can
 * be put to sleep.
 */
static void sep_dequeuer(void *data)
{
	struct crypto_queue *this_queue;
	struct crypto_async_request *async_req;
	struct crypto_async_request *backlog;
	struct ablkcipher_request *cypher_req;
	struct ahash_request *hash_req;
	struct sep_system_ctx *sctx;
	struct crypto_ahash *hash_tfm;
	struct this_task_ctx *ta_ctx;


	this_queue = (struct crypto_queue *)data;

	spin_lock_irq(&queue_lock);
	backlog = crypto_get_backlog(this_queue);
	async_req = crypto_dequeue_request(this_queue);
	spin_unlock_irq(&queue_lock);

	if (!async_req) {
		pr_debug("sep crypto queue is empty\n");
		return;
	}

	if (backlog) {
		pr_debug("sep crypto backlog set\n");
		if (backlog->complete)
			backlog->complete(backlog, -EINPROGRESS);
		backlog = NULL;
	}

	if (!async_req->tfm) {
		pr_debug("sep crypto queue null tfm\n");
		return;
	}

	if (!async_req->tfm->__crt_alg) {
		pr_debug("sep crypto queue null __crt_alg\n");
		return;
	}

	if (!async_req->tfm->__crt_alg->cra_type) {
		pr_debug("sep crypto queue null cra_type\n");
		return;
	}

	/* we have stuff in the queue */
	if (async_req->tfm->__crt_alg->cra_type !=
		&crypto_ahash_type) {
		/* This is for a cypher */
		pr_debug("sep crypto queue doing cipher\n");
		cypher_req = container_of(async_req,
			struct ablkcipher_request,
			base);
		if (!cypher_req) {
			pr_debug("sep crypto queue null cypher_req\n");
			return;
		}

		sep_crypto_block((void *)cypher_req);
		return;
	} else {
		/* This is a hash */
		pr_debug("sep crypto queue doing hash\n");
		/**
		 * This is a bit more complex than cipher; we
		 * need to figure out what type of operation
		 */
		hash_req = ahash_request_cast(async_req);
		if (!hash_req) {
			pr_debug("sep crypto queue null hash_req\n");
			return;
		}

		hash_tfm = crypto_ahash_reqtfm(hash_req);
		if (!hash_tfm) {
			pr_debug("sep crypto queue null hash_tfm\n");
			return;
		}


		sctx = crypto_ahash_ctx(hash_tfm);
		if (!sctx) {
			pr_debug("sep crypto queue null sctx\n");
			return;
		}

		ta_ctx = ahash_request_ctx(hash_req);

		if (ta_ctx->current_hash_stage == HASH_INIT) {
			pr_debug("sep crypto queue hash init\n");
			sep_hash_init((void *)hash_req);
			return;
		} else if (ta_ctx->current_hash_stage == HASH_UPDATE) {
			pr_debug("sep crypto queue hash update\n");
			sep_hash_update((void *)hash_req);
			return;
		} else if (ta_ctx->current_hash_stage == HASH_FINISH) {
			pr_debug("sep crypto queue hash final\n");
			sep_hash_final((void *)hash_req);
			return;
		} else if (ta_ctx->current_hash_stage == HASH_DIGEST) {
			pr_debug("sep crypto queue hash digest\n");
			sep_hash_digest((void *)hash_req);
			return;
		} else if (ta_ctx->current_hash_stage == HASH_FINUP_DATA) {
			pr_debug("sep crypto queue hash digest\n");
			sep_hash_update((void *)hash_req);
			return;
		} else if (ta_ctx->current_hash_stage == HASH_FINUP_FINISH) {
			pr_debug("sep crypto queue hash digest\n");
			sep_hash_final((void *)hash_req);
			return;
		} else {
			pr_debug("sep crypto queue hash oops nothing\n");
			return;
		}
	}
}

static int sep_sha1_init(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha1 init\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA1;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA1;
	ta_ctx->current_hash_stage = HASH_INIT;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha1_update(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha1 update\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA1;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA1;
	ta_ctx->current_hash_stage = HASH_UPDATE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha1_final(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha1 final\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA1;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA1;
	ta_ctx->current_hash_stage = HASH_FINISH;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha1_digest(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha1 digest\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA1;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA1;
	ta_ctx->current_hash_stage = HASH_DIGEST;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha1_finup(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha1 finup\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA1;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA1;
	ta_ctx->current_hash_stage = HASH_FINUP_DATA;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_md5_init(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing md5 init\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = MD5;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_MD5;
	ta_ctx->current_hash_stage = HASH_INIT;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_md5_update(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing md5 update\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = MD5;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_MD5;
	ta_ctx->current_hash_stage = HASH_UPDATE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_md5_final(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing md5 final\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = MD5;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_MD5;
	ta_ctx->current_hash_stage = HASH_FINISH;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_md5_digest(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing md5 digest\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = MD5;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_MD5;
	ta_ctx->current_hash_stage = HASH_DIGEST;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_md5_finup(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing md5 finup\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = MD5;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_MD5;
	ta_ctx->current_hash_stage = HASH_FINUP_DATA;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha224_init(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha224 init\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA224;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA224;
	ta_ctx->current_hash_stage = HASH_INIT;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha224_update(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha224 update\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA224;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA224;
	ta_ctx->current_hash_stage = HASH_UPDATE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha224_final(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha224 final\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA224;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA224;
	ta_ctx->current_hash_stage = HASH_FINISH;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha224_digest(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha224 digest\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA224;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA224;
	ta_ctx->current_hash_stage = HASH_DIGEST;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha224_finup(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha224 finup\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA224;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA224;
	ta_ctx->current_hash_stage = HASH_FINUP_DATA;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha256_init(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha256 init\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA256;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA256;
	ta_ctx->current_hash_stage = HASH_INIT;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha256_update(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha256 update\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA256;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA256;
	ta_ctx->current_hash_stage = HASH_UPDATE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha256_final(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);
	pr_debug("sep - doing sha256 final\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA256;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA256;
	ta_ctx->current_hash_stage = HASH_FINISH;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha256_digest(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha256 digest\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA256;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA256;
	ta_ctx->current_hash_stage = HASH_DIGEST;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_sha256_finup(struct ahash_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ahash_request_ctx(req);

	pr_debug("sep - doing sha256 finup\n");

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = SHA256;
	ta_ctx->current_hash_req = req;
	ta_ctx->current_cypher_req = NULL;
	ta_ctx->hash_opmode = SEP_HASH_SHA256;
	ta_ctx->current_hash_stage = HASH_FINUP_DATA;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_crypto_init(struct crypto_tfm *tfm)
{
	const char *alg_name = crypto_tfm_alg_name(tfm);

	if (alg_name == NULL)
		pr_debug("sep_crypto_init alg is NULL\n");
	else
		pr_debug("sep_crypto_init alg is %s\n", alg_name);

	tfm->crt_ablkcipher.reqsize = sizeof(struct this_task_ctx);
	return 0;
}

static void sep_crypto_exit(struct crypto_tfm *tfm)
{
	pr_debug("sep_crypto_exit\n");
}

static int sep_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
	unsigned int keylen)
{
	struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(tfm);

	pr_debug("sep aes setkey\n");

	pr_debug("tfm is %p sctx is %p\n", tfm, sctx);
	switch (keylen) {
	case SEP_AES_KEY_128_SIZE:
		sctx->aes_key_size = AES_128;
		break;
	case SEP_AES_KEY_192_SIZE:
		sctx->aes_key_size = AES_192;
		break;
	case SEP_AES_KEY_256_SIZE:
		sctx->aes_key_size = AES_256;
		break;
	case SEP_AES_KEY_512_SIZE:
		sctx->aes_key_size = AES_512;
		break;
	default:
		pr_debug("invalid sep aes key size %x\n",
			keylen);
		return -EINVAL;
	}

	memset(&sctx->key.aes, 0, sizeof(u32) *
		SEP_AES_MAX_KEY_SIZE_WORDS);
	memcpy(&sctx->key.aes, key, keylen);
	sctx->keylen = keylen;
	/* Indicate to encrypt/decrypt function to send key to SEP */
	sctx->key_sent = 0;

	return 0;
}

static int sep_aes_ecb_encrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing aes ecb encrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = AES_ECB;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->aes_encmode = SEP_AES_ENCRYPT;
	ta_ctx->aes_opmode = SEP_AES_ECB;
	ta_ctx->init_opcode = SEP_AES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_aes_ecb_decrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing aes ecb decrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = AES_ECB;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->aes_encmode = SEP_AES_DECRYPT;
	ta_ctx->aes_opmode = SEP_AES_ECB;
	ta_ctx->init_opcode = SEP_AES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_aes_cbc_encrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);
	struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(
		crypto_ablkcipher_reqtfm(req));

	pr_debug("sep - doing aes cbc encrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	pr_debug("tfm is %p sctx is %p and ta_ctx is %p\n",
		crypto_ablkcipher_reqtfm(req), sctx, ta_ctx);

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = AES_CBC;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->aes_encmode = SEP_AES_ENCRYPT;
	ta_ctx->aes_opmode = SEP_AES_CBC;
	ta_ctx->init_opcode = SEP_AES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_aes_cbc_decrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);
	struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(
		crypto_ablkcipher_reqtfm(req));

	pr_debug("sep - doing aes cbc decrypt\n");

	pr_debug("tfm is %p sctx is %p and ta_ctx is %p\n",
		crypto_ablkcipher_reqtfm(req), sctx, ta_ctx);

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = AES_CBC;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->aes_encmode = SEP_AES_DECRYPT;
	ta_ctx->aes_opmode = SEP_AES_CBC;
	ta_ctx->init_opcode = SEP_AES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
	unsigned int keylen)
{
	struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(tfm);
	struct crypto_tfm *ctfm = crypto_ablkcipher_tfm(tfm);
	u32 *flags  = &ctfm->crt_flags;

	pr_debug("sep des setkey\n");

	switch (keylen) {
	case DES_KEY_SIZE:
		sctx->des_nbr_keys = DES_KEY_1;
		break;
	case DES_KEY_SIZE * 2:
		sctx->des_nbr_keys = DES_KEY_2;
		break;
	case DES_KEY_SIZE * 3:
		sctx->des_nbr_keys = DES_KEY_3;
		break;
	default:
		pr_debug("invalid key size %x\n",
			keylen);
		return -EINVAL;
	}

	if ((*flags & CRYPTO_TFM_REQ_WEAK_KEY) &&
		(sep_weak_key(key, keylen))) {

		*flags |= CRYPTO_TFM_RES_WEAK_KEY;
		pr_debug("weak key\n");
		return -EINVAL;
	}

	memset(&sctx->key.des, 0, sizeof(struct sep_des_key));
	memcpy(&sctx->key.des.key1, key, keylen);
	sctx->keylen = keylen;
	/* Indicate to encrypt/decrypt function to send key to SEP */
	sctx->key_sent = 0;

	return 0;
}

static int sep_des_ebc_encrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing des ecb encrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = DES_ECB;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->des_encmode = SEP_DES_ENCRYPT;
	ta_ctx->des_opmode = SEP_DES_ECB;
	ta_ctx->init_opcode = SEP_DES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_des_ebc_decrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing des ecb decrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = DES_ECB;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->des_encmode = SEP_DES_DECRYPT;
	ta_ctx->des_opmode = SEP_DES_ECB;
	ta_ctx->init_opcode = SEP_DES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_des_cbc_encrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing des cbc encrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = DES_CBC;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->des_encmode = SEP_DES_ENCRYPT;
	ta_ctx->des_opmode = SEP_DES_CBC;
	ta_ctx->init_opcode = SEP_DES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static int sep_des_cbc_decrypt(struct ablkcipher_request *req)
{
	int error;
	int error1;
	struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req);

	pr_debug("sep - doing des ecb decrypt\n");

	/* Clear out task context */
	memset(ta_ctx, 0, sizeof(struct this_task_ctx));

	ta_ctx->sep_used = sep_dev;
	ta_ctx->current_request = DES_CBC;
	ta_ctx->current_hash_req = NULL;
	ta_ctx->current_cypher_req = req;
	ta_ctx->des_encmode = SEP_DES_DECRYPT;
	ta_ctx->des_opmode = SEP_DES_CBC;
	ta_ctx->init_opcode = SEP_DES_INIT_OPCODE;
	ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE;

	/* lock necessary so that only one entity touches the queues */
	spin_lock_irq(&queue_lock);
	error = crypto_enqueue_request(&sep_queue, &req->base);

	if ((error != 0) && (error != -EINPROGRESS))
		pr_debug(" sep - crypto enqueue failed: %x\n",
			error);
	error1 = sep_submit_work(ta_ctx->sep_used->workqueue,
		sep_dequeuer, (void *)&sep_queue);
	if (error1)
		pr_debug(" sep - workqueue submit failed: %x\n",
			error1);
	spin_unlock_irq(&queue_lock);
	/* We return result of crypto enqueue */
	return error;
}

static struct ahash_alg hash_algs[] = {
{
	.init		= sep_sha1_init,
	.update		= sep_sha1_update,
	.final		= sep_sha1_final,
	.digest		= sep_sha1_digest,
	.finup		= sep_sha1_finup,
	.halg		= {
		.digestsize	= SHA1_DIGEST_SIZE,
		.base	= {
		.cra_name		= "sha1",
		.cra_driver_name	= "sha1-sep",
		.cra_priority		= 100,
		.cra_flags		= CRYPTO_ALG_TYPE_AHASH |
						CRYPTO_ALG_ASYNC,
		.cra_blocksize		= SHA1_BLOCK_SIZE,
		.cra_ctxsize		= sizeof(struct sep_system_ctx),
		.cra_alignmask		= 0,
		.cra_module		= THIS_MODULE,
		.cra_init		= sep_hash_cra_init,
		.cra_exit		= sep_hash_cra_exit,
		}
	}
},
{
	.init		= sep_md5_init,
	.update		= sep_md5_update,
	.final		= sep_md5_final,
	.digest		= sep_md5_digest,
	.finup		= sep_md5_finup,
	.halg		= {
		.digestsize	= MD5_DIGEST_SIZE,
		.base	= {
		.cra_name		= "md5",
		.cra_driver_name	= "md5-sep",
		.cra_priority		= 100,
		.cra_flags		= CRYPTO_ALG_TYPE_AHASH |
						CRYPTO_ALG_ASYNC,
		.cra_blocksize		= SHA1_BLOCK_SIZE,
		.cra_ctxsize		= sizeof(struct sep_system_ctx),
		.cra_alignmask		= 0,
		.cra_module		= THIS_MODULE,
		.cra_init		= sep_hash_cra_init,
		.cra_exit		= sep_hash_cra_exit,
		}
	}
},
{
	.init		= sep_sha224_init,
	.update		= sep_sha224_update,
	.final		= sep_sha224_final,
	.digest		= sep_sha224_digest,
	.finup		= sep_sha224_finup,
	.halg		= {
		.digestsize	= SHA224_DIGEST_SIZE,
		.base	= {
		.cra_name		= "sha224",
		.cra_driver_name	= "sha224-sep",
		.cra_priority		= 100,
		.cra_flags		= CRYPTO_ALG_TYPE_AHASH |
						CRYPTO_ALG_ASYNC,
		.cra_blocksize		= SHA224_BLOCK_SIZE,
		.cra_ctxsize		= sizeof(struct sep_system_ctx),
		.cra_alignmask		= 0,
		.cra_module		= THIS_MODULE,
		.cra_init		= sep_hash_cra_init,
		.cra_exit		= sep_hash_cra_exit,
		}
	}
},
{
	.init		= sep_sha256_init,
	.update		= sep_sha256_update,
	.final		= sep_sha256_final,
	.digest		= sep_sha256_digest,
	.finup		= sep_sha256_finup,
	.halg		= {
		.digestsize	= SHA256_DIGEST_SIZE,
		.base	= {
		.cra_name		= "sha256",
		.cra_driver_name	= "sha256-sep",
		.cra_priority		= 100,
		.cra_flags		= CRYPTO_ALG_TYPE_AHASH |
						CRYPTO_ALG_ASYNC,
		.cra_blocksize		= SHA256_BLOCK_SIZE,
		.cra_ctxsize		= sizeof(struct sep_system_ctx),
		.cra_alignmask		= 0,
		.cra_module		= THIS_MODULE,
		.cra_init		= sep_hash_cra_init,
		.cra_exit		= sep_hash_cra_exit,
		}
	}
}
};

static struct crypto_alg crypto_algs[] = {
{
	.cra_name		= "ecb(aes)",
	.cra_driver_name	= "ecb-aes-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= AES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= AES_MIN_KEY_SIZE,
		.max_keysize	= AES_MAX_KEY_SIZE,
		.setkey		= sep_aes_setkey,
		.encrypt	= sep_aes_ecb_encrypt,
		.decrypt	= sep_aes_ecb_decrypt,
	}
},
{
	.cra_name		= "cbc(aes)",
	.cra_driver_name	= "cbc-aes-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= AES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= AES_MIN_KEY_SIZE,
		.max_keysize	= AES_MAX_KEY_SIZE,
		.setkey		= sep_aes_setkey,
		.encrypt	= sep_aes_cbc_encrypt,
		.ivsize		= AES_BLOCK_SIZE,
		.decrypt	= sep_aes_cbc_decrypt,
	}
},
{
	.cra_name		= "ebc(des)",
	.cra_driver_name	= "ebc-des-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= DES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= DES_KEY_SIZE,
		.max_keysize	= DES_KEY_SIZE,
		.setkey		= sep_des_setkey,
		.encrypt	= sep_des_ebc_encrypt,
		.decrypt	= sep_des_ebc_decrypt,
	}
},
{
	.cra_name		= "cbc(des)",
	.cra_driver_name	= "cbc-des-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= DES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= DES_KEY_SIZE,
		.max_keysize	= DES_KEY_SIZE,
		.setkey		= sep_des_setkey,
		.encrypt	= sep_des_cbc_encrypt,
		.ivsize		= DES_BLOCK_SIZE,
		.decrypt	= sep_des_cbc_decrypt,
	}
},
{
	.cra_name		= "ebc(des3-ede)",
	.cra_driver_name	= "ebc-des3-ede-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= DES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= DES3_EDE_KEY_SIZE,
		.max_keysize	= DES3_EDE_KEY_SIZE,
		.setkey		= sep_des_setkey,
		.encrypt	= sep_des_ebc_encrypt,
		.decrypt	= sep_des_ebc_decrypt,
	}
},
{
	.cra_name		= "cbc(des3-ede)",
	.cra_driver_name	= "cbc-des3--ede-sep",
	.cra_priority		= 100,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
	.cra_blocksize		= DES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct sep_system_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= sep_crypto_init,
	.cra_exit		= sep_crypto_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= DES3_EDE_KEY_SIZE,
		.max_keysize	= DES3_EDE_KEY_SIZE,
		.setkey		= sep_des_setkey,
		.encrypt	= sep_des_cbc_encrypt,
		.decrypt	= sep_des_cbc_decrypt,
	}
}
};

int sep_crypto_setup(void)
{
	int err, i, j, k;
	tasklet_init(&sep_dev->finish_tasklet, sep_finish,
		(unsigned long)sep_dev);

	crypto_init_queue(&sep_queue, SEP_QUEUE_LENGTH);

	sep_dev->workqueue = create_singlethread_workqueue(
		"sep_crypto_workqueue");
	if (!sep_dev->workqueue) {
		dev_warn(&sep_dev->pdev->dev, "cant create workqueue\n");
		return -ENOMEM;
	}

	spin_lock_init(&queue_lock);

	err = 0;
	for (i = 0; i < ARRAY_SIZE(hash_algs); i++) {
		err = crypto_register_ahash(&hash_algs[i]);
		if (err)
			goto err_algs;
	}

	err = 0;
	for (j = 0; j < ARRAY_SIZE(crypto_algs); j++) {
		err = crypto_register_alg(&crypto_algs[j]);
		if (err)
			goto err_crypto_algs;
	}

	return err;

err_algs:
	for (k = 0; k < i; k++)
		crypto_unregister_ahash(&hash_algs[k]);
	return err;

err_crypto_algs:
	for (k = 0; k < j; k++)
		crypto_unregister_alg(&crypto_algs[k]);
	goto err_algs;
}

void sep_crypto_takedown(void)
{

	int i;

	for (i = 0; i < ARRAY_SIZE(hash_algs); i++)
		crypto_unregister_ahash(&hash_algs[i]);
	for (i = 0; i < ARRAY_SIZE(crypto_algs); i++)
		crypto_unregister_alg(&crypto_algs[i]);

	tasklet_kill(&sep_dev->finish_tasklet);
}

#endif