summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Marklund <robert.marklund@stericsson.com>2010-10-07 20:05:22 +0200
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:04:34 +0200
commit14e8ceccf703058389f1a0b6cb8ebb1e73149dc6 (patch)
tree5246ea1e1ca2342a2b190b2d06faf8f296a589c2
parent3416b97dc49ca05087870066ba991a727133b9bd (diff)
HCL driver implementation (hash).
Major refactoring, checkpatch fixes, documentation of functions, structures, enums. Also moved a lot of code from hash_alg.c to hash_core.c. Fixed problem with incorrect digest when doing HMAC calculations. The problem was that the function that did message pad seems to handle padding for keys incorrectly. Now we do not say how many valid bits there are in the last word when it comes to the key and then the hardware seems to handle the key correctly. Fixed contextsaving so hmac(sha1) passes. There was an if statement checking the DINF bit which was never set. I have removed this and then contextsaving for hmac(sha1) is working. Code up to date with the new arch/arm/mach-ux500 folder structure. Signed-off-by: Joakim Bech <joakim.xx.bech@stericsson.com> ux500: switch to DMAENGINE-based DMA driver Switch all DMA clients to the DMA Engine API, and add the platform hooks for the DMA Engine-based DMA40 driver. Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Robert Marklund <robert.marklund@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r--drivers/crypto/Kconfig11
-rw-r--r--drivers/crypto/Makefile1
-rwxr-xr-xdrivers/crypto/ux500/Kconfig15
-rwxr-xr-xdrivers/crypto/ux500/Makefile11
-rwxr-xr-xdrivers/crypto/ux500/hash/Makefile9
-rwxr-xr-xdrivers/crypto/ux500/hash/hash_alg.h476
-rwxr-xr-xdrivers/crypto/ux500/hash/hash_alg_p.h26
-rwxr-xr-xdrivers/crypto/ux500/hash/hash_core.c1756
8 files changed, 2305 insertions, 0 deletions
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index dd414d9350e..52e0bf5738e 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -296,4 +296,15 @@ config CRYPTO_DEV_TEGRA_AES
To compile this driver as a module, choose M here: the module
will be called tegra-aes.
+config CRYPTO_DEV_UX500
+ tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
+ #depends on ARCH_U8500
+ select CRYPTO_ALGAPI
+ help
+ Driver for ST-Ericsson UX500 crypto engine.
+
+if CRYPTO_DEV_UX500
+ source "drivers/crypto/ux500/Kconfig"
+endif # if CRYPTO_DEV_UX500
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index f3e64eadd7a..8737ed1bdfe 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
+obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
diff --git a/drivers/crypto/ux500/Kconfig b/drivers/crypto/ux500/Kconfig
new file mode 100755
index 00000000000..4ac419757d0
--- /dev/null
+++ b/drivers/crypto/ux500/Kconfig
@@ -0,0 +1,15 @@
+
+config CRYPTO_DEV_UX500_HASH
+ tristate "UX500 crypto driver for HASH block"
+ depends on ARCH_U8500
+ select CRYPTO_ALGAPI
+ select CRYPTO_HASH
+ select CRYPTO_HMAC
+ help
+ This selects the UX500 hash driver for the HASH hardware.
+ Depends on U8500/STM DMA if running in DMA mode.
+
+config CRYPTO_DEV_UX500_DEBUG_INFO
+ tristate "Enable UX500 crypto drivers debug info"
+ help
+ This is to enable the debug info for UX500 crypto drivers.
diff --git a/drivers/crypto/ux500/Makefile b/drivers/crypto/ux500/Makefile
new file mode 100755
index 00000000000..4c187857120
--- /dev/null
+++ b/drivers/crypto/ux500/Makefile
@@ -0,0 +1,11 @@
+
+ifeq ($(CONFIG_CRYPTO_DEV_UX500_DEBUG_INFO),y)
+ EXTRA_CFLAGS += -D__DEBUG
+else
+ EXTRA_CFLAGS += -D__RELEASE
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += hash/
+
+
+
diff --git a/drivers/crypto/ux500/hash/Makefile b/drivers/crypto/ux500/hash/Makefile
new file mode 100755
index 00000000000..613330a4ca4
--- /dev/null
+++ b/drivers/crypto/ux500/hash/Makefile
@@ -0,0 +1,9 @@
+
+ifeq ($(CONFIG_CRYPTO_DEV_UX500_DEBUG_INFO),y)
+ EXTRA_CFLAGS += -D__DEBUG
+else
+ EXTRA_CFLAGS += -D__RELEASE
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += u8500_hash.o
+u8500_hash-objs := hash_core.o
diff --git a/drivers/crypto/ux500/hash/hash_alg.h b/drivers/crypto/ux500/hash/hash_alg.h
new file mode 100755
index 00000000000..e1f7c2eb60b
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_alg.h
@@ -0,0 +1,476 @@
+#ifndef _HASH_ALG_H
+#define _HASH_ALG_H
+/*
+ * Copyright (C) 2010 ST-Ericsson.
+ * Copyright (C) 2010 STMicroelectronics.
+ *
+ * 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; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <mach/hcl_defs.h>
+
+/* Number of bytes the message digest */
+#define HASH_MSG_DIGEST_SIZE 32
+#define HASH_BLOCK_SIZE 64
+
+#define __HASH_ENHANCED
+
+/* Version defines */
+#define HASH_HCL_VERSION_ID 1
+#define HASH_HCL_MAJOR_ID 2
+#define HASH_HCL_MINOR_ID 1
+
+#define MAX_HASH_DEVICE 2
+
+/* Maximum value of the length's high word */
+#define HASH_HIGH_WORD_MAX_VAL 0xFFFFFFFFUL
+
+/* Power on Reset values HASH registers */
+#define HASH_RESET_CONTROL_REG_VALUE 0x0
+#define HASH_RESET_START_REG_VALUE 0x0
+
+/* Number of context swap registers */
+#define HASH_CSR_COUNT 52
+
+#define HASH_RESET_CSRX_REG_VALUE 0x0
+#define HASH_RESET_CSFULL_REG_VALUE 0x0
+#define HASH_RESET_CSDATAIN_REG_VALUE 0x0
+
+#define HASH_RESET_INDEX_VAL 0x0
+#define HASH_RESET_BIT_INDEX_VAL 0x0
+#define HASH_RESET_BUFFER_VAL 0x0
+#define HASH_RESET_LEN_HIGH_VAL 0x0
+#define HASH_RESET_LEN_LOW_VAL 0x0
+
+/* Control register bitfields */
+#define HASH_CR_RESUME_MASK 0x11FCF
+
+#define HASH_CR_SWITCHON_POS 31
+#define HASH_CR_SWITCHON_MASK MASK_BIT31
+
+#define HASH_CR_EMPTYMSG_POS 20
+#define HASH_CR_EMPTYMSG_MASK MASK_BIT20
+
+#define HASH_CR_DINF_POS 12
+#define HASH_CR_DINF_MASK MASK_BIT12
+
+#define HASH_CR_NBW_POS 8
+#define HASH_CR_NBW_MASK 0x00000F00UL
+
+#define HASH_CR_LKEY_POS 16
+#define HASH_CR_LKEY_MASK MASK_BIT16
+
+#define HASH_CR_ALGO_POS 7
+#define HASH_CR_ALGO_MASK MASK_BIT7
+
+#define HASH_CR_MODE_POS 6
+#define HASH_CR_MODE_MASK MASK_BIT6
+
+#define HASH_CR_DATAFORM_POS 4
+#define HASH_CR_DATAFORM_MASK (MASK_BIT4 | MASK_BIT5)
+
+#define HASH_CR_DMAE_POS 3
+#define HASH_CR_DMAE_MASK MASK_BIT3
+
+#define HASH_CR_INIT_POS 2
+#define HASH_CR_INIT_MASK MASK_BIT2
+
+#define HASH_CR_PRIVN_POS 1
+#define HASH_CR_PRIVN_MASK MASK_BIT1
+
+#define HASH_CR_SECN_POS 0
+#define HASH_CR_SECN_MASK MASK_BIT0
+
+/* Start register bitfields */
+#define HASH_STR_DCAL_POS 8
+#define HASH_STR_DCAL_MASK MASK_BIT8
+
+#define HASH_STR_NBLW_POS 0
+#define HASH_STR_NBLW_MASK 0x0000001FUL
+
+#define HASH_NBLW_MAX_VAL 0x1F
+
+/* PrimeCell IDs */
+#define HASH_P_ID0 0xE0
+#define HASH_P_ID1 0x05
+#define HASH_P_ID2 0x38
+#define HASH_P_ID3 0x00
+#define HASH_CELL_ID0 0x0D
+#define HASH_CELL_ID1 0xF0
+#define HASH_CELL_ID2 0x05
+#define HASH_CELL_ID3 0xB1
+
+#define HASH_SET_DIN(val) HCL_WRITE_REG(g_sys_ctx.registry[hid]->din, (val))
+
+#define HASH_INITIALIZE \
+ HCL_WRITE_BITS( \
+ g_sys_ctx.registry[hid]->cr, \
+ 0x01 << HASH_CR_INIT_POS, \
+ HASH_CR_INIT_MASK)
+
+#define HASH_SET_DATA_FORMAT(data_format) \
+ HCL_WRITE_BITS( \
+ g_sys_ctx.registry[hid]->cr, \
+ (u32) (data_format) << HASH_CR_DATAFORM_POS, \
+ HASH_CR_DATAFORM_MASK)
+
+#define HASH_GET_HX(pos) \
+ HCL_READ_REG(g_sys_ctx.registry[hid]->hx[pos])
+
+#define HASH_SET_HX(pos, val) \
+ HCL_WRITE_REG(g_sys_ctx.registry[hid]->hx[pos], (val));
+
+#define HASH_SET_NBLW(val) \
+ HCL_WRITE_BITS( \
+ g_sys_ctx.registry[hid]->str, \
+ (u32) (val) << HASH_STR_NBLW_POS, \
+ HASH_STR_NBLW_MASK)
+
+#define HASH_SET_DCAL \
+ HCL_WRITE_BITS( \
+ g_sys_ctx.registry[hid]->str, \
+ 0x01 << HASH_STR_DCAL_POS, \
+ HASH_STR_DCAL_MASK)
+
+/**
+ * struct uint64 - Structure to handle 64 bits integers.
+ * @high_word: Most significant bits
+ * @high_word: Least significant bits
+ *
+ * Used to handle 64 bits integers.
+ */
+struct uint64 {
+ u32 high_word;
+ u32 low_word;
+};
+
+/**
+ * struct hash_register - Contains all registers in u8500 hash hardware.
+ * @cr: HASH control register (0x000)
+ * @din: HASH data input register (0x004)
+ * @str: HASH start register (0x008)
+ * @hx: HASH digest register 0..7 (0x00c-0x01C)
+ * @padding0: Reserved (0x02C)
+ * @itcr: Integration test control register (0x080)
+ * @itip: Integration test input register (0x084)
+ * @itop: Integration test output register (0x088)
+ * @padding1: Reserved (0x08C)
+ * @csfull: HASH context full register (0x0F8)
+ * @csdatain: HASH context swap data input register (0x0FC)
+ * @csrx: HASH context swap register 0..51 (0x100-0x1CC)
+ * @padding2: Reserved (0x1D0)
+ * @periphid0: HASH peripheral identification register 0 (0xFE0)
+ * @periphid1: HASH peripheral identification register 1 (0xFE4)
+ * @periphid2: HASH peripheral identification register 2 (0xFE8)
+ * @periphid3: HASH peripheral identification register 3 (0xFEC)
+ * @cellid0: HASH PCell identification register 0 (0xFF0)
+ * @cellid1: HASH PCell identification register 1 (0xFF4)
+ * @cellid2: HASH PCell identification register 2 (0xFF8)
+ * @cellid3: HASH PCell identification register 3 (0xFFC)
+ *
+ * The device communicates to the HASH via 32-bit-wide control registers
+ * accessible via the 32-bit width AMBA rev. 2.0 AHB Bus. Below is a structure
+ * with the registers used.
+ */
+struct hash_register {
+ u32 cr;
+ u32 din;
+ u32 str;
+ u32 hx[8];
+
+ u32 padding0[(0x080 - 0x02C) >> 2];
+
+ u32 itcr;
+ u32 itip;
+ u32 itop;
+
+ u32 padding1[(0x0F8 - 0x08C) >> 2];
+
+ u32 csfull;
+ u32 csdatain;
+ u32 csrx[HASH_CSR_COUNT];
+
+ u32 padding2[(0xFE0 - 0x1D0) >> 2];
+
+ u32 periphid0;
+ u32 periphid1;
+ u32 periphid2;
+ u32 periphid3;
+
+ u32 cellid0;
+ u32 cellid1;
+ u32 cellid2;
+ u32 cellid3;
+};
+
+/**
+ * struct hash_state - Hash context state.
+ * @temp_cr: Temporary HASH Control Register
+ * @str_reg: HASH Start Register
+ * @din_reg: HASH Data Input Register
+ * @csr[52]: HASH Context Swap Registers 0-39
+ * @csfull: HASH Context Swap Registers 40 ie Status flags
+ * @csdatain: HASH Context Swap Registers 41 ie Input data
+ * @buffer: Working buffer for messages going to the hardware
+ * @length: Length of the part of the message hashed so far (floor(N/64) * 64)
+ * @index: Valid number of bytes in buffer (N % 64)
+ * @bit_index: Valid number of bits in buffer (N % 8)
+ *
+ * This structure is used between context switches, i.e. when ongoing jobs are
+ * interupted with new jobs. When this happens we need to store intermediate
+ * results in software.
+ *
+ * WARNING: "index" is the member of the structure, to be sure that "buffer"
+ * is aligned on a 4-bytes boundary. This is highly implementation dependent
+ * and MUST be checked whenever this code is ported on new platforms.
+ */
+struct hash_state {
+ u32 temp_cr;
+ u32 str_reg;
+ u32 din_reg;
+ u32 csr[52];
+ u32 csfull;
+ u32 csdatain;
+ u32 buffer[HASH_BLOCK_SIZE / sizeof(u32)];
+ struct uint64 length;
+ u8 index;
+ u8 bit_index;
+};
+
+/**
+ * struct hash_system_context - Structure for the global system context.
+ * @registry: Pointer to the registry of the hash hardware
+ * @state: State of the hash device
+ */
+struct hash_system_context {
+ /* Pointer to HASH registers structure */
+ volatile struct hash_register *registry[MAX_HASH_DEVICE];
+
+ /* State of HASH device */
+ struct hash_state state[MAX_HASH_DEVICE];
+};
+
+/**
+ * enum hash_device_id - HASH device ID.
+ * @HASH_DEVICE_ID_0: Hash hardware with ID 0
+ * @HASH_DEVICE_ID_1: Hash hardware with ID 1
+ */
+enum hash_device_id {
+ HASH_DEVICE_ID_0 = 0,
+ HASH_DEVICE_ID_1 = 1
+};
+
+/**
+ * enum hash_data_format - HASH data format.
+ * @HASH_DATA_32_BITS: 32 bits data format
+ * @HASH_DATA_16_BITS: 16 bits data format
+ * @HASH_DATA_8_BITS: 8 bits data format
+ * @HASH_DATA_1_BITS: 1 bit data format
+ */
+enum hash_data_format {
+ HASH_DATA_32_BITS = 0x0,
+ HASH_DATA_16_BITS = 0x1,
+ HASH_DATA_8_BITS = 0x2,
+ HASH_DATA_1_BIT = 0x3
+};
+
+/**
+ * enum hash_device_state - Device state
+ * @DISABLE: Disable the hash hardware
+ * @ENABLE: Enable the hash hardware
+ */
+enum hash_device_state {
+ DISABLE = 0,
+ ENABLE = 1
+};
+
+/**
+ * struct hash_protection_config - Device protection configuration.
+ * @privilege_access: FIXME, add comment.
+ * @secure_access: FIXME, add comment.
+ */
+struct hash_protection_config {
+ int privilege_access;
+ int secure_access;
+};
+
+/**
+ * enum hash_input_status - Data Input flag status.
+ * @HASH_DIN_EMPTY: Indicates that nothing is in data registers
+ * @HASH_DIN_FULL: Indicates that data registers are full
+ */
+enum hash_input_status {
+ HASH_DIN_EMPTY = 0,
+ HASH_DIN_FULL = 1
+};
+
+/**
+ * Number of words already pushed
+ */
+enum hash_nbw_pushed {
+ HASH_NBW_00 = 0x00,
+ HASH_NBW_01 = 0x01,
+ HASH_NBW_02 = 0x02,
+ HASH_NBW_03 = 0x03,
+ HASH_NBW_04 = 0x04,
+ HASH_NBW_05 = 0x05,
+ HASH_NBW_06 = 0x06,
+ HASH_NBW_07 = 0x07,
+ HASH_NBW_08 = 0x08,
+ HASH_NBW_09 = 0x09,
+ HASH_NBW_10 = 0x0A,
+ HASH_NBW_11 = 0x0B,
+ HASH_NBW_12 = 0x0C,
+ HASH_NBW_13 = 0x0D,
+ HASH_NBW_14 = 0x0E,
+ HASH_NBW_15 = 0x0F
+};
+
+/**
+ * struct hash_device_status - Device status for DINF, NBW, and NBLW bit
+ * fields.
+ * @dinf_status: HASH data in full flag
+ * @nbw_status: Number of words already pushed
+ * @nblw_status: Number of Valid Bits Last Word of the Message
+ */
+struct hash_device_status {
+ int dinf_status;
+ int nbw_status;
+ u8 nblw_status;
+};
+
+/**
+ * enum hash_dma_request - Enumeration for HASH DMA request types.
+ */
+enum hash_dma_request {
+ HASH_DISABLE_DMA_REQ = 0x0,
+ HASH_ENABLE_DMA_REQ = 0x1
+};
+
+/**
+ * enum hash_digest_cal - Enumeration for digest calculation.
+ * @HASH_DISABLE_DCAL: Indicates that DCAL bit is not set/used.
+ * @HASH_ENABLE_DCAL: Indicates that DCAL bit is set/used.
+ */
+enum hash_digest_cal {
+ HASH_DISABLE_DCAL = 0x0,
+ HASH_ENABLE_DCAL = 0x1
+};
+
+/**
+ * enum hash_algo - Enumeration for selecting between SHA1 or SHA2 algorithm
+ * @HASH_ALGO_SHA1: Indicates that SHA1 is used.
+ * @HASH_ALGO_SHA2: Indicates that SHA2 (SHA256) is used.
+ */
+enum hash_algo {
+ HASH_ALGO_SHA1 = 0x0,
+ HASH_ALGO_SHA2 = 0x1
+};
+
+/**
+ * enum hash_op - Enumeration for selecting between HASH or HMAC mode
+ * @HASH_OPER_MODE_HASH: Indicates usage of normal HASH mode
+ * @HASH_OPER_MODE_HMAC: Indicates usage of HMAC
+ */
+enum hash_op {
+ HASH_OPER_MODE_HASH = 0x0,
+ HASH_OPER_MODE_HMAC = 0x1
+};
+
+/**
+ * enum hash_key_type - Enumeration for selecting between long and short key.
+ * @HASH_SHORT_KEY: Key used is shorter or equal to block size (64 bytes)
+ * @HASH_LONG_KEY: Key used is greater than block size (64 bytes)
+ */
+enum hash_key_type {
+ HASH_SHORT_KEY = 0x0,
+ HASH_LONG_KEY = 0x1
+};
+
+/**
+ * struct hash_config - Configuration data for the hardware
+ * @data_format: Format of data entered into the hash data in register
+ * @algorithm: Algorithm selection bit
+ * @oper_mode: Operating mode selection bit
+ * @hmac_key: Long key selection bit HMAC mode
+ */
+struct hash_config {
+ int data_format;
+ int algorithm;
+ int oper_mode;
+ int hmac_key;
+};
+
+
+/**
+ * enum hash_error - Error codes for hash.
+ */
+enum hash_error {
+ HASH_OK = 0,
+ HASH_MSG_LENGTH_OVERFLOW,
+ HASH_INTERNAL_ERROR,
+ HASH_NOT_CONFIGURED,
+ HASH_REQUEST_PENDING,
+ HASH_REQUEST_NOT_APPLICABLE,
+ HASH_INVALID_PARAMETER,
+ HASH_UNSUPPORTED_FEATURE,
+ HASH_UNSUPPORTED_HW
+};
+
+int hash_init_base_address(int hash_device_id, t_logical_address base_address);
+
+int HASH_GetVersion(t_version *p_version);
+
+int HASH_Reset(int hash_devive_id);
+
+int HASH_ConfigureDmaRequest(int hash_device_id, int request_state);
+
+int HASH_ConfigureLastValidBits(int hash_device_id, u8 nblw_val);
+
+int HASH_ConfigureDigestCal(int hash_device_id, int dcal_state);
+
+int HASH_ConfigureProtection(int hash_device_id,
+ struct hash_protection_config
+ *p_protect_config);
+
+int hash_setconfiguration(int hash_device_id, struct hash_config *p_config);
+
+int hash_begin(int hash_device_id);
+
+int hash_get_digest(int hash_device_id, u8 digest[HASH_MSG_DIGEST_SIZE]);
+
+int HASH_ClockGatingOff(int hash_device_id);
+
+struct hash_device_status HASH_GetDeviceStatus(int hash_device_id);
+
+t_bool HASH_IsDcalOngoing(int hash_device_id);
+
+int hash_hw_update(int hash_device_id,
+ const u8 *p_data_buffer,
+ u32 msg_length);
+
+int hash_end(int hash_device_id, u8 digest[HASH_MSG_DIGEST_SIZE]);
+
+int hash_compute(int hash_device_id,
+ const u8 *p_data_buffer,
+ u32 msg_length,
+ struct hash_config *p_hash_config,
+ u8 digest[HASH_MSG_DIGEST_SIZE]);
+
+int hash_end_key(int hash_device_id);
+
+int hash_save_state(int hash_device_id, struct hash_state *state);
+
+int hash_resume_state(int hash_device_id, const struct hash_state *state);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/drivers/crypto/ux500/hash/hash_alg_p.h b/drivers/crypto/ux500/hash/hash_alg_p.h
new file mode 100755
index 00000000000..c85faaeba6f
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_alg_p.h
@@ -0,0 +1,26 @@
+/*****************************************************************************/
+/**
+* � ST-Ericsson, 2009 - All rights reserved
+* Reproduction and Communication of this document is strictly prohibited
+* unless specifically authorized in writing by ST-Ericsson
+*
+* static Header file of HASH Processor
+* Specification release related to this implementation: A_V2.2
+* AUTHOR : ST-Ericsson
+*/
+/*****************************************************************************/
+
+#ifndef _HASH_P_H_
+#define _HASH_P_H_
+
+/*--------------------------------------------------------------------------*
+ * Includes *
+ *--------------------------------------------------------------------------*/
+#include "hash_alg.h"
+
+/*--------------------------------------------------------------------------*
+ * Defines *
+ *--------------------------------------------------------------------------*/
+
+#endif /* End _HASH_P_H_ */
+
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
new file mode 100755
index 00000000000..fd5f8a870bf
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -0,0 +1,1756 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for Nomadik hardware crypto engine.
+ *
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/crypto.h>
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+
+#include <mach/debug.h>
+#include <mach/hardware.h>
+
+#include "hash_alg.h"
+
+#define DRIVER_NAME "DRIVER HASH"
+/* enables/disables debug msgs */
+#define DRIVER_DEBUG 1
+#define DRIVER_DEBUG_PFX DRIVER_NAME
+#define DRIVER_DBG KERN_ERR
+
+#define MAX_HASH_DIGEST_BYTE_SIZE 32
+#define HASH_BLOCK_BYTE_SIZE 64
+
+#define HASH_ACC_SYNC_CONTROL
+#ifdef HASH_ACC_SYNC_CONTROL
+static struct mutex hash_hw_acc_mutex;
+#endif
+
+int debug;
+static int mode;
+static int contextsaving;
+static struct hash_system_context g_sys_ctx;
+
+/**
+ * struct hash_driver_data - IO Base and clock.
+ * @base: The IO base for the block
+ * @clk: FIXME, add comment
+ */
+struct hash_driver_data {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+/**
+ * struct hash_ctx - The context used for hash calculations.
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ * @updated: Indicates if hardware is initialized for new operations
+ * @state: The state of the current calculations
+ * @config: The current configuration
+ */
+struct hash_ctx {
+ u8 key[HASH_BLOCK_BYTE_SIZE];
+ u32 keylen;
+ u8 updated;
+ struct hash_state state;
+ struct hash_config config;
+};
+
+/**
+ * struct hash_tfm_ctx - Transform context
+ * @key: The key stored in the transform context
+ * @keylen: The length of the key in the transform context
+ */
+struct hash_tfm_ctx {
+ u8 key[HASH_BLOCK_BYTE_SIZE];
+ u32 keylen;
+};
+
+/* Declaration of functions */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes);
+
+/**
+ * hexdump - Dumps buffers in hex.
+ * @buf: The buffer to dump
+ * @len: The length of the buffer
+ */
+static void hexdump(unsigned char *buf, unsigned int len)
+{
+ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, len, false);
+}
+
+/**
+ * clear_reg_str - Clear the registry hash_str.
+ * @hid: Hardware device ID
+ *
+ * This function will clear the dcal bit and the nblw bits.
+ */
+static inline void clear_reg_str(int hid)
+{
+ /* We will only clear the valid registers and not the reserved */
+ g_sys_ctx.registry[hid]->str &= ~HASH_STR_DCAL_MASK;
+ g_sys_ctx.registry[hid]->str &= ~HASH_STR_NBLW_MASK;
+}
+
+/**
+ * write_nblw - Writes the number of valid bytes to nblw.
+ * @hid: Hardware device ID
+ * @bytes: The number of valid bytes in last word of a message
+ *
+ * Note that this function only writes, i.e. it does not clear the registry
+ * before it writes the new data.
+ */
+static inline void write_nblw(int hid, int bytes)
+{
+ g_sys_ctx.registry[hid]->str |=
+ ((bytes * 8) & HASH_STR_NBLW_MASK);
+}
+
+/**
+ * write_dcal - Write/set the dcal bit.
+ * @hid: Hardware device ID
+ */
+static inline void write_dcal(int hid)
+{
+ g_sys_ctx.registry[hid]->str |= (1 << HASH_STR_DCAL_POS);
+}
+
+/**
+ * pad_message - Function that pads a message.
+ * @hid: Hardware device ID
+ *
+ * FIXME: This function should be replaced.
+ */
+static inline void pad_message(int hid)
+{
+ hash_messagepad(hid, g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+}
+
+/**
+ * write_key - Writes the key to the hardware registries.
+ * @hid: Hardware device ID
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ *
+ * Note that in this function we DO NOT write to the NBLW registry even though
+ * the hardware reference manual says so. There must be incorrect information in
+ * the manual or there must be a bug in the state machine in the hardware.
+ */
+static void write_key(int hid, const u8 *key, u32 keylen)
+{
+ u32 word = 0;
+ clear_reg_str(hid);
+
+ while (keylen >= 4) {
+ word = ((u32) (key[3] & 255) << 24) |
+ ((u32) (key[2] & 255) << 16) |
+ ((u32) (key[1] & 255) << 8) |
+ ((u32) (key[0] & 255));
+
+ HASH_SET_DIN(word);
+ keylen -= 4;
+ key += 4;
+ }
+
+ /* This takes care of the remaining bytes on the last word */
+ if (keylen) {
+ word = 0;
+ while (keylen) {
+ word |= (key[keylen - 1] << (8 * (keylen - 1)));
+ keylen--;
+ }
+ HASH_SET_DIN(word);
+ }
+
+ write_dcal(hid);
+}
+
+/**
+ * init_hash_hw - Initialise the hash hardware for a new calculation.
+ * @desc: The hash descriptor for the job
+ *
+ * This function will enable the bits needed to clear and start a new
+ * calculation.
+ */
+static int init_hash_hw(struct shash_desc *desc)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[init_hash_hw] (ctx=0x%x)!", (u32)ctx);
+
+ hash_error = hash_setconfiguration(HASH_DEVICE_ID_1, &ctx->config);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_setconfiguration() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ hash_error = hash_begin(HASH_DEVICE_ID_1);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_begin() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC) {
+ stm_dbg(debug, "[init_hash_hw] update key=0x%0x, len=%d",
+ (u32) ctx->key, ctx->keylen);
+ write_key(HASH_DEVICE_ID_1, ctx->key, ctx->keylen);
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * hash_init - Common hash init function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ *
+ * Initialize structures and copy the key from the transform context to the
+ * descriptor context if the mode is HMAC.
+ */
+static int hash_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+ struct hash_tfm_ctx *tfm_ctx = crypto_tfm_ctx(&desc->tfm->base);
+
+ stm_dbg(debug, "[hash_init]: (ctx=0x%x)!", (u32)ctx);
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC) {
+ if (tfm_ctx->key) {
+ memcpy(ctx->key, tfm_ctx->key, tfm_ctx->keylen);
+ ctx->keylen = tfm_ctx->keylen;
+ }
+ }
+
+ memset(&ctx->state, 0, sizeof(struct hash_state));
+ ctx->updated = 0;
+
+ return 0;
+}
+
+/**
+ * hash_update - The hash update function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @data: Message that should be hashed
+ * @len: The length of the message that should be hashed
+ */
+static int hash_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hash_update]: (ctx=0x%x, data=0x%x, len=%d)!",
+ (u32)ctx, (u32)data, len);
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+
+ if (!ctx->updated) {
+ ret = init_hash_hw(desc);
+ if (ret) {
+ stm_error("init_hash_hw() failed!");
+ goto out;
+ }
+ }
+
+ if (contextsaving) {
+ if (ctx->updated) {
+ hash_error =
+ hash_resume_state(HASH_DEVICE_ID_1, &ctx->state);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_resume_state() failed!");
+ ret = -1;
+ goto out;
+ }
+ }
+ }
+
+ /* NOTE: The length of the message is in the form of number of bits */
+ hash_error = hash_hw_update(HASH_DEVICE_ID_1, data, len * 8);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_hw_update() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ if (contextsaving) {
+ hash_error =
+ hash_save_state(HASH_DEVICE_ID_1, &ctx->state);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_save_state() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ }
+ ctx->updated = 1;
+
+out:
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+ return ret;
+}
+
+/**
+ * hash_final - The hash final function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @out: Pointer for the calculated digest
+ */
+static int hash_final(struct shash_desc *desc, u8 *out)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ int digestsize = crypto_shash_digestsize(desc->tfm);
+ u8 digest[HASH_MSG_DIGEST_SIZE];
+
+ stm_dbg(debug, "[hash_final]: (ctx=0x%x)!", (u32) ctx);
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+
+ if (contextsaving) {
+ hash_error = hash_resume_state(HASH_DEVICE_ID_1, &ctx->state);
+
+ if (hash_error != HASH_OK) {
+ stm_error("hash_resume_state() failed!");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ pad_message(HASH_DEVICE_ID_1);
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
+ write_key(HASH_DEVICE_ID_1, ctx->key, ctx->keylen);
+
+ hash_error = hash_get_digest(HASH_DEVICE_ID_1, digest);
+
+ memcpy(out, digest, digestsize);
+
+out:
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+
+ return ret;
+}
+
+/**
+ * hash_setkey - The setkey function for providing the key during HMAC
+ * calculations.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ * @alg: The algorithm to use in the operation
+ */
+static int hash_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen, int alg)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+
+ struct hash_tfm_ctx *ctx_tfm = crypto_shash_ctx(tfm);
+
+ stm_dbg(debug, "[hash_setkey]: (ctx_tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) ctx_tfm, (u32) key, keylen);
+
+ /* Truncate the key to block size */
+ if (keylen > HASH_BLOCK_BYTE_SIZE) {
+ struct hash_config config;
+ u8 digest[MAX_HASH_DIGEST_BYTE_SIZE];
+ unsigned int digestsize = crypto_shash_digestsize(tfm);
+
+ config.algorithm = alg;
+ config.data_format = HASH_DATA_8_BITS;
+ config.oper_mode = HASH_OPER_MODE_HASH;
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+ hash_error = hash_compute(HASH_DEVICE_ID_1, key, keylen * 8,
+ &config, digest);
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+ if (hash_error != HASH_OK) {
+ stm_error("Error: hash_compute() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ memcpy(ctx_tfm->key, digest, digestsize);
+ ctx_tfm->keylen = digestsize;
+ } else {
+ memcpy(ctx_tfm->key, key, keylen);
+ ctx_tfm->keylen = keylen;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * sha1_init - SHA1 init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha1_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[sha1_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA1;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+/**
+ * sha256_init - SHA2 (SHA256) init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha256_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[sha256_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA2;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha1_init - SHA1 HMAC init function.
+ * @desc: The hash descriptor for the job
+ */
+static int hmac_sha1_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hmac_sha1_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA1;
+ ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
+ ctx->config.hmac_key = HASH_SHORT_KEY;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha256_init - SHA2 (SHA256) HMAC init function.
+ * @desc: The hash descriptor for the job
+ */
+static int hmac_sha256_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hmac_sha256_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA2;
+ ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
+ ctx->config.hmac_key = HASH_SHORT_KEY;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha1_setkey - SHA1 HMAC setkey function.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ */
+static int hmac_sha1_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ stm_dbg(debug, "[hmac_sha1_setkey]: (tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) tfm, (u32) key, keylen);
+
+ return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA1);
+}
+
+/**
+ * hmac_sha256_setkey - SHA2 (SHA256) HMAC setkey function.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ */
+static int hmac_sha256_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ stm_dbg(debug, "[hmac_sha256_setkey]: (tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) tfm, (u32) key, keylen);
+
+ return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA2);
+}
+
+static struct shash_alg sha1_alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .update = hash_update,
+ .final = hash_final,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "sha1-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg sha256_alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = sha256_init,
+ .update = hash_update,
+ .final = hash_final,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "sha256-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg hmac_sha1_alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = hmac_sha1_init,
+ .update = hash_update,
+ .final = hash_final,
+ .setkey = hmac_sha1_setkey,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "hmac(sha1)",
+ .cra_driver_name = "hmac(sha1-u8500)",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg hmac_sha256_alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = hmac_sha256_init,
+ .update = hash_update,
+ .final = hash_final,
+ .setkey = hmac_sha256_setkey,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "hmac(sha256-u8500)",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+/**
+ * u8500_hash_probe - Function that probes the hash hardware.
+ * @pdev: The platform device
+ */
+static int u8500_hash_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct resource *res = NULL;
+ struct hash_driver_data *hash_drv_data;
+
+ stm_dbg(debug, "[u8500_hash_probe]: (pdev=0x%x)", (u32) pdev);
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling kzalloc()!");
+ hash_drv_data = kzalloc(sizeof(struct hash_driver_data), GFP_KERNEL);
+ if (!hash_drv_data) {
+ stm_dbg(debug, "kzalloc() failed!");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ stm_dbg(debug, "platform_get_resource() failed");
+ ret = -ENODEV;
+ goto out_kfree;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling request_mem_region()!");
+ res = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (res == NULL) {
+ stm_dbg(debug, "request_mem_region() failed");
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling ioremap()!");
+ hash_drv_data->base = ioremap(res->start, res->end - res->start + 1);
+ if (!hash_drv_data->base) {
+ stm_error
+ ("[u8500_hash] ioremap of hash1 register memory failed!");
+ ret = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling clk_get()!");
+ /* Enable the clk for HASH1 hardware block */
+ hash_drv_data->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(hash_drv_data->clk)) {
+ stm_error("clk_get() failed!");
+ ret = PTR_ERR(hash_drv_data->clk);
+ goto out_unmap;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling clk_enable()!");
+ ret = clk_enable(hash_drv_data->clk);
+ if (ret) {
+ stm_error("clk_enable() failed!");
+ goto out_unmap;
+ }
+
+ stm_dbg(debug,
+ "[u8500_hash_probe]: Calling hash_init_base_address()->"
+ "(base=0x%x,DEVICE_ID=%d)!",
+ (u32) hash_drv_data->base, HASH_DEVICE_ID_1);
+
+ /* Setting base address */
+ hash_error =
+ hash_init_base_address(HASH_DEVICE_ID_1,
+ (t_logical_address) hash_drv_data->base);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_init_base_address() failed!");
+ ret = -1; /*TODO: what error code should be used here!? */
+ goto out_clk;
+ }
+#ifdef HASH_ACC_SYNC_CONTROL
+ stm_dbg(debug, "[u8500_hash_probe]: Calling mutex_init()!");
+ mutex_init(&hash_hw_acc_mutex);
+#endif
+
+ if (mode == 0) {
+ stm_dbg(debug,
+ "[u8500_hash_probe]: To register all algorithms!");
+
+ ret = crypto_register_shash(&sha1_alg);
+ if (ret) {
+ stm_error("Could not register sha1_alg!");
+ goto out_clk;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: sha1_alg registered!");
+
+ ret = crypto_register_shash(&sha256_alg);
+ if (ret) {
+ stm_error("Could not register sha256_alg!");
+ goto out_unreg1;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: sha256_alg registered!");
+
+ ret = crypto_register_shash(&hmac_sha1_alg);
+ if (ret) {
+ stm_error("Could not register hmac_sha1_alg!");
+ goto out_unreg2;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: hmac_sha1_alg registered!");
+
+ ret = crypto_register_shash(&hmac_sha256_alg);
+ if (ret) {
+ stm_error("Could not register hmac_sha256_alg!");
+ goto out_unreg3;
+ }
+ stm_dbg(debug,
+ "[u8500_hash_probe]: hmac_sha256_alg registered!");
+ }
+
+ if (mode == 10) {
+ stm_dbg(debug,
+ "[u8500_hash_probe]: To register only sha1 and sha256"
+ " algorithms!");
+
+ ret = crypto_register_shash(&sha1_alg);
+ if (ret) {
+ stm_error("Could not register sha1_alg!");
+ goto out_clk;
+ }
+
+ ret = crypto_register_shash(&sha256_alg);
+ if (ret) {
+ stm_error("Could not register sha256_alg!");
+ goto out_unreg1_tmp;
+ }
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling platform_set_drvdata()!");
+ platform_set_drvdata(pdev, hash_drv_data);
+ return 0;
+
+ if (mode == 0) {
+out_unreg1:
+ crypto_unregister_shash(&sha1_alg);
+out_unreg2:
+ crypto_unregister_shash(&sha256_alg);
+out_unreg3:
+ crypto_unregister_shash(&hmac_sha1_alg);
+ }
+
+ if (mode == 10) {
+out_unreg1_tmp:
+ crypto_unregister_shash(&sha1_alg);
+ }
+
+out_clk:
+ clk_disable(hash_drv_data->clk);
+ clk_put(hash_drv_data->clk);
+
+out_unmap:
+ iounmap(hash_drv_data->base);
+
+out_free_mem:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+out_kfree:
+ kfree(hash_drv_data);
+out:
+ return ret;
+}
+
+/**
+ * u8500_hash_remove - Function that removes the hash device from the platform.
+ * @pdev: The platform device
+ */
+static int u8500_hash_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct hash_driver_data *hash_drv_data;
+
+ stm_dbg(debug, "[u8500_hash_remove]: (pdev=0x%x)", (u32) pdev);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling platform_get_drvdata()!");
+ hash_drv_data = platform_get_drvdata(pdev);
+
+ if (mode == 0) {
+ stm_dbg(debug,
+ "[u8500_hash_remove]: To unregister all algorithms!");
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+ crypto_unregister_shash(&hmac_sha1_alg);
+ crypto_unregister_shash(&hmac_sha256_alg);
+ }
+
+ if (mode == 10) {
+ stm_dbg(debug,
+ "[u8500_hash_remove]: To unregister only sha1 and "
+ "sha256 algorithms!");
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+ }
+#ifdef HASH_ACC_SYNC_CONTROL
+ stm_dbg(debug, "[u8500_hash_remove]: Calling mutex_destroy()!");
+ mutex_destroy(&hash_hw_acc_mutex);
+#endif
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling clk_disable()!");
+ clk_disable(hash_drv_data->clk);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling clk_put()!");
+ clk_put(hash_drv_data->clk);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling iounmap(): base = 0x%x",
+ (u32) hash_drv_data->base);
+ iounmap(hash_drv_data->base);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ stm_dbg(debug,
+ "[u8500_hash_remove]: Calling release_mem_region()"
+ "->res->start=0x%x, res->end = 0x%x!",
+ res->start, res->end);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling kfree()!");
+ kfree(hash_drv_data);
+
+ return 0;
+}
+
+static struct platform_driver hash_driver = {
+ .probe = u8500_hash_probe,
+ .remove = u8500_hash_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hash1",
+ },
+};
+
+/**
+ * u8500_hash_mod_init - The kernel module init function.
+ */
+static int __init u8500_hash_mod_init(void)
+{
+ stm_dbg(debug, "u8500_hash_mod_init() is called!");
+
+ return platform_driver_register(&hash_driver);
+}
+
+/**
+ * u8500_hash_mod_fini - The kernel module exit function.
+ */
+static void __exit u8500_hash_mod_fini(void)
+{
+ stm_dbg(debug, "u8500_hash_mod_fini() is called!");
+
+ platform_driver_unregister(&hash_driver);
+ return;
+}
+
+/**
+ * hash_processblock - This function processes a single block of 512 bits (64
+ * bytes), word aligned, starting at message.
+ * @hid: Hardware device ID
+ * @message: Block (512 bits) of message to be written to the HASH hardware
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_processblock(int hid, const u32 *message)
+{
+ u32 count;
+
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_DCAL_MASK);
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_NBLW_MASK);
+
+ /* Partially unrolled loop */
+ for (count = 0; count < (HASH_BLOCK_SIZE / sizeof(u32));
+ count += 4) {
+ HASH_SET_DIN(message[0]);
+ HASH_SET_DIN(message[1]);
+ HASH_SET_DIN(message[2]);
+ HASH_SET_DIN(message[3]);
+ message += 4;
+ }
+}
+
+/**
+ * hash_messagepad - Pads a message and write the nblw bits.
+ * @hid: Hardware device ID
+ * @message: Last word of a message
+ * @index_bytes: The number of bytes in the last message
+ *
+ * This function manages the final part of the digest calculation, when less
+ * than 512 bits (64 bytes) remain in message. This means index_bytes < 64.
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes)
+{
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad"
+ "(bytes in final msg=%d))", index_bytes);
+
+ clear_reg_str(hid);
+
+ /* Main loop */
+ while (index_bytes >= 4) {
+ HASH_SET_DIN(message[0]);
+ index_bytes -= 4;
+ message++;
+ }
+
+ if (index_bytes)
+ HASH_SET_DIN(message[0]);
+
+ /* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */
+ HASH_SET_NBLW(index_bytes * 8);
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad -> DIN=0x%08x NBLW=%d",
+ g_sys_ctx.registry[hid]->din,
+ g_sys_ctx.registry[hid]->str);
+ HASH_SET_DCAL;
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad d -> "
+ "DIN=0x%08x NBLW=%d",
+ g_sys_ctx.registry[hid]->din,
+ g_sys_ctx.registry[hid]->str);
+
+}
+
+/**
+ * hash_incrementlength - Increments the length of the current message.
+ * @hid: Hardware device ID
+ * @incr: Length of message processed already
+ *
+ * Overflow cannot occur, because conditions for overflow are checked in
+ * hash_hw_update.
+ */
+static void hash_incrementlength(int hid, u32 incr)
+{
+ g_sys_ctx.state[hid].length.low_word += incr;
+
+ /* Check for wrap-around */
+ if (g_sys_ctx.state[hid].length.low_word < incr)
+ g_sys_ctx.state[hid].length.high_word++;
+}
+
+/**
+ * hash_setconfiguration - Sets the required configuration for the hash
+ * hardware.
+ * @hid: Hardware device ID
+ * @p_config: Pointer to a configuration structure
+ *
+ * Reentrancy: Non Re-entrant
+ * Reentrancy issues:
+ * 1. Global variable registry(cofiguration register,
+ * parameter register, divider register) is being modified
+ *
+ * Comments 1. : User need to call hash_begin API after calling this
+ * API i.e. the current configuration is set only when
+ * bit INIT is set and we set INIT bit in hash_begin.
+ * Changing the configuration during a computation has
+ * no effect so we first set configuration by calling
+ * this API and then set the INIT bit for the HASH
+ * processor and the curent configuration is taken into
+ * account. As reading INIT bit (with correct protection
+ * rights) will always return 0b so we can't make a check
+ * at software level. So the user has to initialize the
+ * device for new configuration to take in to effect.
+ * 2. The default value of data format is 00b ie the format
+ * of data entered in HASH_DIN register is 32-bit data.
+ * The data written in HASH_DIN is used directly by the
+ * HASH processing, without re ordering.
+ */
+int hash_setconfiguration(int hid, struct hash_config *p_config)
+{
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_setconfiguration())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ HASH_SET_DATA_FORMAT(p_config->data_format);
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_EMPTYMSG_MASK);
+
+ /* This bit selects between SHA-1 or SHA-2 algorithm */
+ if (HASH_ALGO_SHA2 == p_config->algorithm) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_ALGO_MASK);
+ } else { /* SHA1 algorithm */
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_ALGO_MASK);
+ }
+
+ /* This bit selects between HASH or HMAC mode for the selected
+ algorithm */
+ if (HASH_OPER_MODE_HASH == p_config->oper_mode) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry
+ [hid]->cr, HASH_CR_MODE_MASK);
+ } else { /* HMAC mode */
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_MODE_MASK);
+
+ /* This bit selects between short key (<= 64 bytes) or long key
+ (>64 bytes) in HMAC mode */
+ if (HASH_SHORT_KEY == p_config->hmac_key) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_LKEY_MASK);
+ } else {
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_LKEY_MASK);
+ }
+ }
+
+ return hash_error;
+}
+
+/**
+ * hash_begin - This routine resets some globals and initializes the hash
+ * hardware.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant
+ *
+ * Comments 1. : User need to call hash_setconfiguration API before
+ * calling this API i.e. the current configuration is set
+ * only when bit INIT is set and we set INIT bit in
+ * hash_begin. Changing the configuration during a
+ * computation has no effect so we first set
+ * configuration by calling this API and then set the
+ * INIT bit for the HASH processor and the current
+ * configuration is taken into account. As reading INIT
+ * bit (with correct protection rights) will always
+ * return 0b so we can't make a check at software level.
+ * So the user has to initialize the device for new
+ * configuration to take in to effect.
+ */
+int hash_begin(int hid)
+{
+ int hash_error = HASH_OK;
+
+ /* HW and SW initializations */
+ /* Note: there is no need to initialize buffer and digest members */
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_begin())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+
+ HASH_INITIALIZE;
+
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_DCAL_MASK);
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_NBLW_MASK);
+
+ return hash_error;
+}
+
+/**
+ * hash_hw_update - Updates current HASH computation hashing another part of
+ * the message.
+ * @hid: Hardware device ID
+ * @p_data_buffer: Byte array containing the message to be hashed (caller
+ * allocated)
+ * @msg_length: Length of message to be hashed (in bits)
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_hw_update(int hid, const u8 *p_data_buffer, u32 msg_length)
+{
+ int hash_error = HASH_OK;
+ u8 index;
+ u8 *p_buffer;
+ u32 count;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_hw_update(msg_length=%d / %d), "
+ "in=%d, bin=%d))",
+ msg_length,
+ msg_length / 8,
+ g_sys_ctx.state[hid].index,
+ g_sys_ctx.state[hid].bit_index);
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ index = g_sys_ctx.state[hid].index;
+
+ p_buffer = (u8 *)g_sys_ctx.state[hid].buffer;
+
+ /* Number of bytes in the message */
+ msg_length /= 8;
+
+ /* Check parameters */
+ if (NULL == p_data_buffer) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Check if g_sys_ctx.state.length + msg_length
+ overflows */
+ if (msg_length >
+ (g_sys_ctx.state[hid].length.low_word + msg_length)
+ && HASH_HIGH_WORD_MAX_VAL ==
+ (g_sys_ctx.state[hid].length.high_word)) {
+ hash_error = HASH_MSG_LENGTH_OVERFLOW;
+ stm_error("[u8500_hash_alg] HASH_MSG_LENGTH_OVERFLOW!");
+ return hash_error;
+ }
+
+ /* Main loop */
+ while (0 != msg_length) {
+ if ((index + msg_length) < HASH_BLOCK_SIZE) {
+ for (count = 0; count < msg_length; count++) {
+ /*TODO: memcpy? */
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ index += msg_length;
+ msg_length = 0;
+ } else {
+ /* if 'p_data_buffer' is four byte aligned and local
+ * buffer does not have any data, we can write data
+ * directly from 'p_data_buffer' to HW peripheral,
+ * otherwise we first copy data to a local buffer
+ */
+ if ((0 == (((u32) p_data_buffer) % 4))
+ && (0 == index)) {
+ hash_processblock(hid,
+ (const u32 *)p_data_buffer);
+ } else {
+ for (count = 0;
+ count < (u32)(HASH_BLOCK_SIZE - index);
+ count++) {
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ hash_processblock(hid, (const u32 *)p_buffer);
+ }
+
+ hash_incrementlength(hid, HASH_BLOCK_SIZE);
+ p_data_buffer += (HASH_BLOCK_SIZE - index);
+ msg_length -= (HASH_BLOCK_SIZE - index);
+ index = 0;
+ }
+ }
+
+ g_sys_ctx.state[hid].index = index;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_hw_update END(msg_length=%d in "
+ "bits, in=%d, bin=%d))",
+ msg_length,
+ g_sys_ctx.state[hid].index,
+ g_sys_ctx.state[hid].bit_index);
+
+ return hash_error;
+}
+
+/**
+ * hash_end_key - Function that ends a message, i.e. pad and triggers the last
+ * calculation.
+ * @hid: Hardware device ID
+ *
+ * This function also clear the registries that have been involved in
+ * computation.
+ */
+int hash_end_key(int hid)
+{
+ int hash_error = HASH_OK;
+ u8 count = 0;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_end_key(index=%d))",
+ g_sys_ctx.state[hid].index);
+
+ hash_messagepad(hid, g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+
+ /* Wait till the DCAL bit get cleared, So that we get the final
+ * message digest not intermediate value.
+ */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ /* Reset the HASH state */
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32); count++)
+ g_sys_ctx.state[hid].buffer[count] = 0;
+
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+
+ return hash_error;
+}
+
+/**
+ * hash_resume_state - Function that resumes the state of an calculation.
+ * @hid: Hardware device ID
+ * @device_state: The state to be restored in the hash hardware
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_resume_state(int hid, const struct hash_state *device_state)
+{
+ u32 temp_cr;
+ int hash_error = HASH_OK;
+ s32 count;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_resume_state(state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Check correctness of index and length members */
+ if (device_state->index > HASH_BLOCK_SIZE
+ || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ for (count = 0; count < (s32) (HASH_BLOCK_SIZE / sizeof(u32));
+ count++) {
+ g_sys_ctx.state[hid].buffer[count] =
+ device_state->buffer[count];
+ }
+
+ g_sys_ctx.state[hid].index = device_state->index;
+ g_sys_ctx.state[hid].bit_index = device_state->bit_index;
+ g_sys_ctx.state[hid].length = device_state->length;
+
+ HASH_INITIALIZE;
+
+ temp_cr = device_state->temp_cr;
+ g_sys_ctx.registry[hid]->cr =
+ temp_cr & HASH_CR_RESUME_MASK;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36) &&
+ !(g_sys_ctx.registry[hid]->cr &
+ HASH_CR_MODE_MASK)) {
+ break;
+ }
+ g_sys_ctx.registry[hid]->csrx[count] =
+ device_state->csr[count];
+ }
+
+ g_sys_ctx.registry[hid]->csfull = device_state->csfull;
+ g_sys_ctx.registry[hid]->csdatain = device_state->csdatain;
+
+ g_sys_ctx.registry[hid]->str = device_state->str_reg;
+ g_sys_ctx.registry[hid]->cr = temp_cr;
+
+ return hash_error;
+}
+
+/**
+ * hash_save_state - Function that saves the state of hardware.
+ * @hid: Hardware device ID
+ * @device_state: The strucure where the hardware state should be saved
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_save_state(int hid, struct hash_state *device_state)
+{
+ u32 temp_cr;
+ u32 count;
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_save_state( state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32); count++) {
+ device_state->buffer[count] =
+ g_sys_ctx.state[hid].buffer[count];
+ }
+
+ device_state->index = g_sys_ctx.state[hid].index;
+ device_state->bit_index = g_sys_ctx.state[hid].bit_index;
+ device_state->length = g_sys_ctx.state[hid].length;
+
+ /* Write dummy value to force digest intermediate calculation. This
+ * actually makes sure that there isn't any ongoing calculation in the
+ * hardware.
+ */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ temp_cr = g_sys_ctx.registry[hid]->cr;
+
+ device_state->str_reg = g_sys_ctx.registry[hid]->str;
+
+ device_state->din_reg = g_sys_ctx.registry[hid]->din;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36)
+ && !(g_sys_ctx.registry[hid]->cr &
+ HASH_CR_MODE_MASK)) {
+ break;
+ }
+
+ device_state->csr[count] =
+ g_sys_ctx.registry[hid]->csrx[count];
+ }
+
+ device_state->csfull = g_sys_ctx.registry[hid]->csfull;
+ device_state->csdatain = g_sys_ctx.registry[hid]->csdatain;
+
+ /* end if */
+ device_state->temp_cr = temp_cr;
+
+ return hash_error;
+}
+
+/**
+ * hash_end - Ends current HASH computation, passing back the hash to the user.
+ * @hid: Hardware device ID
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_end(int hid, u8 digest[HASH_MSG_DIGEST_SIZE])
+{
+ int hash_error = HASH_OK;
+ u32 count;
+ /* Standard SHA-1 digest for null string for HASH mode */
+ u8 zero_message_hash_sha1[HASH_MSG_DIGEST_SIZE] = {
+ 0xDA, 0x39, 0xA3, 0xEE,
+ 0x5E, 0x6B, 0x4B, 0x0D,
+ 0x32, 0x55, 0xBF, 0xEF,
+ 0x95, 0x60, 0x18, 0x90,
+ 0xAF, 0xD8, 0x07, 0x09,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA-2 digest for null string for HASH mode */
+ u8 zero_message_hash_sha2[HASH_MSG_DIGEST_SIZE] = {
+ 0xD4, 0x1D, 0x8C, 0xD9,
+ 0x8F, 0x00, 0xB2, 0x04,
+ 0xE9, 0x80, 0x09, 0x98,
+ 0xEC, 0xF8, 0x42, 0x7E,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA-1 digest for null string for HMAC mode,with no key */
+ u8 zero_message_hmac_sha1[HASH_MSG_DIGEST_SIZE] = {
+ 0xFB, 0xDB, 0x1D, 0x1B,
+ 0x18, 0xAA, 0x6C, 0x08,
+ 0x32, 0x4B, 0x7D, 0x64,
+ 0xB7, 0x1F, 0xB7, 0x63,
+ 0x70, 0x69, 0x0E, 0x1D,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA2 digest for null string for HMAC mode,with no key */
+ u8 zero_message_hmac_sha2[HASH_MSG_DIGEST_SIZE] = {
+ 0x74, 0xE6, 0xF7, 0x29,
+ 0x8A, 0x9C, 0x2D, 0x16,
+ 0x89, 0x35, 0xF5, 0x8C,
+ 0x00, 0x1B, 0xAD, 0x88,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_end(digest array (0x%x)))",
+ (u32) digest);
+
+ if (NULL == digest) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (0 == g_sys_ctx.state[hid].index &&
+ 0 == g_sys_ctx.state[hid].length.high_word &&
+ 0 == g_sys_ctx.state[hid].length.low_word) {
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_MODE_MASK) {
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_ALGO_MASK) {
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hmac_sha1[count];
+ }
+ } else { /* SHA-2 algo */
+
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hmac_sha2[count];
+ }
+ }
+ } else { /* HASH mode */
+
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_ALGO_MASK) {
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hash_sha1[count];
+ }
+ } else { /* SHA-2 algo */
+
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hash_sha2[count];
+ }
+ }
+ }
+
+ HASH_SET_DCAL;
+ } else {
+ hash_messagepad(hid,
+ g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+
+ /* Wait till the DCAL bit get cleared, So that we get the final
+ * message digest not intermediate value. */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ hash_error = hash_get_digest(hid, digest);
+
+ /* Reset the HASH state */
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32);
+ count++) {
+ g_sys_ctx.state[hid].buffer[count]
+ = 0;
+ }
+
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+ }
+
+ if (debug)
+ hexdump(digest, HASH_MSG_DIGEST_SIZE);
+
+ return hash_error;
+}
+
+/**
+ * hash_initialize_globals - Initialize global variables to their default reset
+ * value.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant, global structure g_sys_ctx elements are being
+ * modified
+ */
+static void hash_initialize_globals(int hid)
+{
+ u8 loop_count;
+
+ /* Resetting the values of global variables except the registry */
+ g_sys_ctx.state[hid].temp_cr = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].str_reg = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].din_reg = HASH_RESET_INDEX_VAL;
+
+ for (loop_count = 0; loop_count < HASH_CSR_COUNT; loop_count++) {
+ g_sys_ctx.state[hid].csr[loop_count] =
+ HASH_RESET_CSRX_REG_VALUE;
+ }
+
+ g_sys_ctx.state[hid].csfull = HASH_RESET_CSFULL_REG_VALUE;
+ g_sys_ctx.state[hid].csdatain = HASH_RESET_CSDATAIN_REG_VALUE;
+
+ for (loop_count = 0; loop_count < (HASH_BLOCK_SIZE / sizeof(u32));
+ loop_count++) {
+ g_sys_ctx.state[hid].buffer[loop_count] =
+ HASH_RESET_BUFFER_VAL;
+ }
+
+ g_sys_ctx.state[hid].length.high_word = HASH_RESET_LEN_HIGH_VAL;
+ g_sys_ctx.state[hid].length.low_word = HASH_RESET_LEN_LOW_VAL;
+ g_sys_ctx.state[hid].index = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].bit_index = HASH_RESET_BIT_INDEX_VAL;
+}
+
+/**
+ * hash_reset - This routine will reset the global variable to default reset
+ * value and HASH registers to their power on reset values.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant, global structure g_sys_ctx elements are being
+ * modified.
+ */
+int hash_reset(int hid)
+{
+ int hash_error = HASH_OK;
+ u8 loop_count;
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+
+ return hash_error;
+ }
+
+ /* Resetting the values of global variables except the registry */
+ hash_initialize_globals(hid);
+
+ /* Resetting HASH control register to power-on-reset values */
+ g_sys_ctx.registry[hid]->str = HASH_RESET_START_REG_VALUE;
+
+ for (loop_count = 0; loop_count < HASH_CSR_COUNT; loop_count++) {
+ g_sys_ctx.registry[hid]->csrx[loop_count] =
+ HASH_RESET_CSRX_REG_VALUE;
+ }
+
+ g_sys_ctx.registry[hid]->csfull = HASH_RESET_CSFULL_REG_VALUE;
+ g_sys_ctx.registry[hid]->csdatain =
+ HASH_RESET_CSDATAIN_REG_VALUE;
+
+ /* Resetting the HASH Control reg. This also reset the PRIVn and SECn
+ * bits and hence the device registers will not be accessed anymore and
+ * should be done in the last HASH register access statement.
+ */
+ g_sys_ctx.registry[hid]->cr = HASH_RESET_CONTROL_REG_VALUE;
+
+ return hash_error;
+}
+
+/**
+ * hash_init_base_address - This routine initializes hash register base
+ * address. It also checks for peripheral Ids and PCell Ids.
+ * @hid: Hardware device ID
+ * @base_address: Hash hardware base address
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (register base address)
+ * is being modified.
+ */
+int hash_init_base_address(int hid, t_logical_address base_address)
+{
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_init_base_address())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+
+ return hash_error;
+ }
+
+ if (0 != base_address) {
+ /*--------------------------------------*
+ * Initializing the registers structure *
+ *--------------------------------------*/
+ g_sys_ctx.registry[hid] = (struct hash_register *) base_address;
+
+ /*--------------------------*
+ * Checking Peripheral Ids *
+ *--------------------------*/
+ if ((HASH_P_ID0 ==
+ g_sys_ctx.registry[hid]->periphid0)
+ && (HASH_P_ID1 ==
+ g_sys_ctx.registry[hid]->periphid1)
+ && (HASH_P_ID2 ==
+ g_sys_ctx.registry[hid]->periphid2)
+ && (HASH_P_ID3 ==
+ g_sys_ctx.registry[hid]->periphid3)
+ && (HASH_CELL_ID0 ==
+ g_sys_ctx.registry[hid]->cellid0)
+ && (HASH_CELL_ID1 ==
+ g_sys_ctx.registry[hid]->cellid1)
+ && (HASH_CELL_ID2 ==
+ g_sys_ctx.registry[hid]->cellid2)
+ && (HASH_CELL_ID3 ==
+ g_sys_ctx.registry[hid]->cellid3)
+ ) {
+
+ /* Resetting the values of global variables except the
+ registry */
+ hash_initialize_globals(hid);
+ hash_error = HASH_OK;
+ return hash_error;
+ } else {
+ hash_error = HASH_UNSUPPORTED_HW;
+ stm_error("[u8500_hash_alg] HASH_UNSUPPORTED_HW!");
+ return hash_error;
+ }
+ } /* end if */
+ else {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+}
+
+/**
+ * hash_get_digest - Gets the digest.
+ * @hid: Hardware device ID
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (hash control register)
+ * is being modified.
+ *
+ * Note that, if this is called before the final message has been handle it will
+ * return the intermediate message digest.
+ */
+int hash_get_digest(int hid, u8 *digest)
+{
+ u32 temp_hx_val, count;
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug,
+ "[u8500_hash_alg] hash_get_digest(digest array:(0x%x))",
+ (u32) digest);
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Copy result into digest array */
+ for (count = 0; count < (HASH_MSG_DIGEST_SIZE / sizeof(u32));
+ count++) {
+ temp_hx_val = HASH_GET_HX(count);
+ digest[count * 4] = (u8) ((temp_hx_val >> 24) & 0xFF);
+ digest[count * 4 + 1] = (u8) ((temp_hx_val >> 16) & 0xFF);
+ digest[count * 4 + 2] = (u8) ((temp_hx_val >> 8) & 0xFF);
+ digest[count * 4 + 3] = (u8) ((temp_hx_val >> 0) & 0xFF);
+ }
+
+ return hash_error;
+}
+
+/**
+ * hash_compute - Performs a complete HASH calculation on the message passed.
+ * @hid: Hardware device ID
+ * @p_data_buffer: Pointer to the message to be hashed
+ * @msg_length: The length of the message
+ * @p_hash_config: Structure with configuration data for the hash hardware
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_compute(int hid,
+ const u8 *p_data_buffer,
+ u32 msg_length,
+ struct hash_config *p_hash_config,
+ u8 digest[HASH_MSG_DIGEST_SIZE]) {
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_compute())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+
+ /* WARNING: return code must be checked if
+ * behaviour of hash_begin changes.
+ */
+ hash_error = hash_setconfiguration(hid, p_hash_config);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_setconfiguration() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_begin(hid);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_begin() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_hw_update(hid, p_data_buffer, msg_length);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_hw_update() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_end(hid, digest);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_end() failed!");
+ return hash_error;
+ }
+
+ return hash_error;
+}
+
+module_init(u8500_hash_mod_init);
+module_exit(u8500_hash_mod_fini);
+
+module_param(mode, int, 0);
+module_param(debug, int, 0);
+module_param(contextsaving, int, 0);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson U8500 HASH engine.");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("sha1-u8500");
+MODULE_ALIAS("sha256-u8500");
+MODULE_ALIAS("hmac(sha1-u8500)");
+MODULE_ALIAS("hmac(sha256-u8500)");