msm8960: Enable crypto for msm8960.

-- Add support for crypto4 engine
-- Enable crypto clk only when crypto driver is used.
Change-Id: Ie50c259ae6e6090782d196af89b3a22776f88913
diff --git a/platform/msm_shared/crypto4_eng.c b/platform/msm_shared/crypto4_eng.c
new file mode 100644
index 0000000..59562c4
--- /dev/null
+++ b/platform/msm_shared/crypto4_eng.c
@@ -0,0 +1,355 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <endian.h>
+#include <debug.h>
+#include <reg.h>
+#include <bits.h>
+#include <platform/iomap.h>
+#include <crypto4_eng.h>
+#include <crypto_hash.h>
+
+extern void dsb(void);
+
+/*
+ * Function to reset the crypto engine.
+ */
+
+void crypto_eng_reset(void)
+{
+	return;
+}
+
+/*
+ * Function to initialize the crypto engine for a new session. It enables the
+ * auto shutdown feature of CRYPTO and mask various interrupts since we use
+ * polling. We are not using DMOV now.
+ */
+
+void crypto_eng_init(void)
+{
+	unsigned int val;
+	val = (AUTO_SHUTDOWN_EN | MASK_ERR_INTR | MASK_DIN_INTR |
+			MASK_DOUT_INTR | HIGH_SPD_IN_EN_N | HIGH_SPD_OUT_EN_N);
+
+	val |= MASK_OP_DONE_INTR;
+
+	wr_ce(val,CRYPTO_CONFIG);
+}
+
+/*
+ * Function to set various SHAx registers in CRYPTO based on algorithm type.
+ */
+
+void crypto_set_sha_ctx(void *ctx_ptr, unsigned int bytes_to_write,
+		crypto_auth_alg_type auth_alg, bool first, bool last)
+{
+	crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx*)ctx_ptr;
+	crypto_SHA256_ctx *sha256_ctx = (crypto_SHA256_ctx*)ctx_ptr;
+	unsigned int i=0;
+	unsigned int iv_len=0;
+	unsigned int *auth_iv;
+	unsigned int seg_cfg_val;
+
+	seg_cfg_val = SEG_CFG_AUTH_ALG_SHA;
+
+	if(auth_alg == CRYPTO_AUTH_ALG_SHA1)
+	{
+		seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA1;
+
+		if(last)
+		{
+			seg_cfg_val |= SEG_CFG_LAST;
+		}
+
+		iv_len = SHA1_INIT_VECTOR_SIZE;
+		auth_iv = sha1_ctx->auth_iv;
+	}
+	else if(auth_alg == CRYPTO_AUTH_ALG_SHA256)
+	{
+		seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA256;
+
+		if(last)
+		{
+			seg_cfg_val |= SEG_CFG_LAST;
+		}
+
+		iv_len = SHA256_INIT_VECTOR_SIZE;
+		auth_iv = sha256_ctx->auth_iv;
+	}
+	else
+	{
+		dprintf(CRITICAL, "crypto_set_sha_ctx invalid auth algorithm\n");
+		return;
+	}
+
+	for(i=0; i<iv_len; i++)
+	{
+		wr_ce(*(auth_iv+i),CRYPTO_AUTH_IVn(i));
+	}
+	wr_ce(seg_cfg_val,CRYPTO_AUTH_SEG_CFG);
+
+	/* Typecast with crypto_SHA1_ctx because offset of auth_bytecnt in both
+	   crypto_SHA1_ctx and crypto_SHA256_ctx are same */
+
+	wr_ce(((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[0],CRYPTO_AUTH_BYTECNTn(0));
+	wr_ce(((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[1],CRYPTO_AUTH_BYTECNTn(1));
+
+	wr_ce(bytes_to_write,CRYPTO_AUTH_SEG_SIZE);
+
+	wr_ce(bytes_to_write,CRYPTO_SEG_SIZE);
+
+	/*
+	 * Ensure previous instructions (any writes to config registers)
+	 * are completed.
+	 *
+	 * TODO: Revisit dsb.
+	 */
+	dsb();
+
+	wr_ce(GOPROC_GO,CRYPTO_GOPROC);
+
+	return;
+}
+
+/*
+ * Function to send data to CRYPTO. This is non-DMOV implementation and uses
+ * polling to send the requested amount of data.
+ */
+
+void crypto_send_data(void *ctx_ptr, unsigned char *data_ptr,
+		unsigned int buff_size, unsigned int bytes_to_write,
+		unsigned int *ret_status)
+{
+	crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx*)ctx_ptr;
+	unsigned int bytes_left=0;
+	unsigned int i=0;
+	unsigned int ce_status=0;
+	unsigned int ce_err_bmsk=0;
+	unsigned int is_not_aligned=FALSE;
+	unsigned char data[4];
+	unsigned char *buff_ptr=data_ptr;
+
+	/* Check if the buff_ptr is aligned */
+	if(!(IS_ALIGNED(buff_ptr)))
+	{
+		is_not_aligned = TRUE;
+	}
+
+	/* Fill the saved_buff with data from buff_ptr. First we have to write
+	   all the data from the saved_buff and then we will write data from
+	   buff_ptr. We will update bytes_left and buff_ptr in the while loop
+	   once are done writing all the data from saved_buff. */
+
+	if(sha1_ctx->saved_buff_indx != 0)
+	{
+		memcpy(sha1_ctx->saved_buff + sha1_ctx->saved_buff_indx, buff_ptr,
+				(((buff_size + sha1_ctx->saved_buff_indx) <= CRYPTO_SHA_BLOCK_SIZE)
+				 ? buff_size : (CRYPTO_SHA_BLOCK_SIZE - sha1_ctx->saved_buff_indx)));
+
+		if(bytes_to_write >= CRYPTO_SHA_BLOCK_SIZE)
+		{
+			bytes_left = CRYPTO_SHA_BLOCK_SIZE;
+		}
+		else
+		{
+			bytes_left = bytes_to_write;
+		}
+	}
+	else
+	{
+		bytes_left = bytes_to_write;
+	}
+
+	/* Error bitmask to check crypto engine status */
+	ce_err_bmsk = (SW_ERR | DIN_RDY | DIN_SIZE_AVAIL);
+
+	while(bytes_left >= 4)
+	{
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+
+		if(ce_status & SW_ERR)
+		{
+			/* If there is SW_ERR, reset the engine */
+			crypto_eng_reset();
+			*ret_status = CRYPTO_ERR_FAIL;
+			dprintf(CRITICAL, "crypto_send_data sw error\n");
+			return;
+		}
+
+		/* We can write data now - 4 bytes at a time in network byte order */
+		if((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4))
+		{
+			if(sha1_ctx->saved_buff_indx != 0)
+			{
+				/* Write from saved_buff */
+				wr_ce(htonl(*((unsigned int *)(sha1_ctx->saved_buff)+i)),CRYPTO_DATA_IN);
+			}
+			else
+			{
+				if(!is_not_aligned)
+				{
+					/* Write from buff_ptr aligned */
+					wr_ce(htonl(*((unsigned int *)buff_ptr+i)),CRYPTO_DATA_IN);
+				}
+				else
+				{
+					/* If buff_ptr is not aligned write byte by byte */
+					data[0] = *(buff_ptr+i);
+					data[1] = *(buff_ptr+i+1);
+					data[2] = *(buff_ptr+i+2);
+					data[3] = *(buff_ptr+i+3);
+					/* i will incremented by 1 in outside block */
+					i+=3;
+					wr_ce(htonl(*(unsigned int *)data),CRYPTO_DATA_IN);
+					memset(data,0,4);
+				}
+			}
+			i++;
+			bytes_left -=4;
+
+			/* Check if we have written from saved_buff. Adjust buff_ptr and
+			   bytes_left accordingly */
+			if((sha1_ctx->saved_buff_indx != 0) && (bytes_left == 0) &&
+					(bytes_to_write > CRYPTO_SHA_BLOCK_SIZE))
+			{
+				bytes_left = (bytes_to_write - CRYPTO_SHA_BLOCK_SIZE);
+				buff_ptr = (unsigned char *)((unsigned char *)data_ptr +
+						CRYPTO_SHA_BLOCK_SIZE - sha1_ctx->saved_buff_indx);
+				i = 0;
+				sha1_ctx->saved_buff_indx = 0;
+				if(!(IS_ALIGNED(buff_ptr)))
+				{
+					is_not_aligned = TRUE;
+				}
+			}
+		}
+	}
+
+	/* We might have bytes_left < 4. Write them now if available */
+	if(bytes_left)
+	{
+		memset(data,0,sizeof(unsigned int));
+
+		if(sha1_ctx->saved_buff_indx)
+			buff_ptr = (sha1_ctx->saved_buff + bytes_to_write - 1);
+		else
+			buff_ptr = (((unsigned char *)data_ptr) + buff_size - 1);
+
+		for(i=0;i<bytes_left;i++)
+		{
+			data[3-i] = *(buff_ptr-bytes_left+i+1);
+		}
+
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+
+		if(ce_status & SW_ERR)
+		{
+			crypto_eng_reset();
+			*ret_status = CRYPTO_ERR_FAIL;
+			dprintf(CRITICAL, "crypto_send_data sw error 2\n");
+			return;
+		}
+		if((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4))
+		{
+			wr_ce(*(unsigned int *)data,CRYPTO_DATA_IN);
+		}
+	}
+	*ret_status = CRYPTO_ERR_NONE;
+	return;
+}
+
+/*
+ * Function to get digest from CRYPTO. We poll for AUTH_DONE from CRYPTO.
+ */
+
+void crypto_get_digest(unsigned char *digest_ptr, unsigned int *ret_status,
+		crypto_auth_alg_type auth_alg, bool last)
+{
+	unsigned int ce_status=0;
+	unsigned int ce_err_bmsk=0;
+	unsigned int i=0;
+	unsigned int digest_len=0;
+
+	ce_err_bmsk = (OPERATION_DONE | SW_ERR);
+
+	do
+	{
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+	}while (ce_status == 0);
+
+	if(ce_status & SW_ERR)
+	{
+		crypto_eng_reset();
+		*ret_status = CRYPTO_ERR_FAIL;
+		dprintf(CRITICAL, "crypto_get_digest sw error\n");
+		return;
+	}
+
+	/* Digest length depends on auth_alg */
+
+	if(auth_alg == CRYPTO_AUTH_ALG_SHA1)
+	{
+		digest_len = SHA1_INIT_VECTOR_SIZE;
+	}
+	else if (auth_alg == CRYPTO_AUTH_ALG_SHA256)
+	{
+		digest_len = SHA256_INIT_VECTOR_SIZE;
+	}
+
+	/* Retrieve digest from CRYPTO */
+
+	for(i=0; i < digest_len;i++)
+	{
+		unsigned int auth_iv = rd_ce(CRYPTO_AUTH_IVn(i));
+
+		if(last)
+		{
+			*((unsigned int *)digest_ptr + i) = htonl(auth_iv);
+		}
+		else
+		{
+			*((unsigned int *)digest_ptr + i) = auth_iv;
+		}
+	}
+	*ret_status = CRYPTO_ERR_NONE;
+	return;
+}
+
+/* Function to restore auth_bytecnt registers for ctx_ptr */
+
+void crypto_get_ctx(void *ctx_ptr)
+{
+	((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[0] = rd_ce(CRYPTO_AUTH_BYTECNTn(0));
+	((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[1] = rd_ce(CRYPTO_AUTH_BYTECNTn(1));
+	return;
+}