From eebb14cb2df3fb6071b217e1b1f571ad2b9a79ad Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 19 Oct 2011 13:21:40 +0200 Subject: crypto: ux500: hash: converted to ahash. - Removed cryp1 and hash1 from u8500_v2_prcc_clocks, added comment that they already are included in u8500_v1_v2_prcc_clocks. - Re-activated hash1 accelerator in u8500__defconfig. - Updated clock-db8500.c with correct clk for hash1. - Added -O0 in hash Makefile, to remove optimization in debug mode. - Converted to ahash (from shash). - Updated infrastructure (klist functionality). - Dependencies to hcl_defs removed. - Changed power_state_mutex to spinlock and also use the atomic regulator interfaces, which removes the kernel printout BUG: sleeping function... - Removed the clear_bit function call and instead use the HASH_CLEAR_BITS macro. - Re-arranged the code to collect the external functions in the bottom of the file. - Removed page_to_phys/phys_to_virt calls in hash_hw_update, data directly accessed in walk.data. - Removed unused defines. ST-Ericsson ID: 319847, 280690 ST-Ericsson Linux next: Not tested, ER 320876 ST-Ericsson FOSS-OUT ID: NA Change-Id: I732b7320cd8302d1dc86e4acb1954880e175773a Signed-off-by: Berne Hebark Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23294 Reviewed-by: Joakim BECH --- drivers/crypto/ux500/hash/Makefile | 2 +- drivers/crypto/ux500/hash/hash_alg.h | 404 ++++--- drivers/crypto/ux500/hash/hash_core.c | 1911 ++++++++++++++++++--------------- 3 files changed, 1242 insertions(+), 1075 deletions(-) diff --git a/drivers/crypto/ux500/hash/Makefile b/drivers/crypto/ux500/hash/Makefile index 6194da8eec8..aaa5f56a2c2 100644 --- a/drivers/crypto/ux500/hash/Makefile +++ b/drivers/crypto/ux500/hash/Makefile @@ -4,7 +4,7 @@ # License terms: GNU General Public License (GPL) version 2 # ifdef CONFIG_CRYPTO_DEV_UX500_DEBUG -CFLAGS_hash_core.o := -DDEBUG +CFLAGS_hash_core.o := -DDEBUG -O0 endif obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += u8500_hash.o diff --git a/drivers/crypto/ux500/hash/hash_alg.h b/drivers/crypto/ux500/hash/hash_alg.h index 1c3dd5705fb..3bf1354ea03 100644 --- a/drivers/crypto/ux500/hash/hash_alg.h +++ b/drivers/crypto/ux500/hash/hash_alg.h @@ -2,140 +2,137 @@ * Copyright (C) ST-Ericsson SA 2010 * Author: Shujuan Chen (shujuan.chen@stericsson.com) * Author: Joakim Bech (joakim.xx.bech@stericsson.com) + * Author: Berne Hebark (berne.hebark@stericsson.com)) * License terms: GNU General Public License (GPL) version 2 */ #ifndef _HASH_ALG_H #define _HASH_ALG_H -#include +#include /* Number of bytes the message digest */ -#define HASH_MSG_DIGEST_SIZE 32 -#define HASH_BLOCK_SIZE 64 +#define HASH_MSG_DIGEST_SIZE 32 +#define HASH_BLOCK_SIZE 64 #define HASH_SHA1_DIGEST_SIZE 20 -#define HASH_SHA2_DIGEST_SIZE 32 - -/* 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 +#define HASH_SHA2_DIGEST_SIZE 32 /* Maximum value of the length's high word */ -#define HASH_HIGH_WORD_MAX_VAL 0xFFFFFFFFUL +#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 +#define HASH_RESET_CR_VALUE 0x0 +#define HASH_RESET_STR_VALUE 0x0 /* Number of context swap registers */ -#define HASH_CSR_COUNT 52 +#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_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 +#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_RESUME_MASK 0x11FCF -#define HASH_CR_SWITCHON_POS 31 -#define HASH_CR_SWITCHON_MASK MASK_BIT31 +#define HASH_CR_SWITCHON_POS 31 +#define HASH_CR_SWITCHON_MASK BIT(31) -#define HASH_CR_EMPTYMSG_POS 20 -#define HASH_CR_EMPTYMSG_MASK MASK_BIT20 +#define HASH_CR_EMPTYMSG_POS 20 +#define HASH_CR_EMPTYMSG_MASK BIT(20) -#define HASH_CR_DINF_POS 12 -#define HASH_CR_DINF_MASK MASK_BIT12 +#define HASH_CR_DINF_POS 12 +#define HASH_CR_DINF_MASK BIT(12) -#define HASH_CR_NBW_POS 8 -#define HASH_CR_NBW_MASK 0x00000F00UL +#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_LKEY_POS 16 +#define HASH_CR_LKEY_MASK BIT(16) -#define HASH_CR_ALGO_POS 7 -#define HASH_CR_ALGO_MASK MASK_BIT7 +#define HASH_CR_ALGO_POS 7 +#define HASH_CR_ALGO_MASK BIT(7) -#define HASH_CR_MODE_POS 6 -#define HASH_CR_MODE_MASK MASK_BIT6 +#define HASH_CR_MODE_POS 6 +#define HASH_CR_MODE_MASK BIT(6) -#define HASH_CR_DATAFORM_POS 4 -#define HASH_CR_DATAFORM_MASK (MASK_BIT4 | MASK_BIT5) +#define HASH_CR_DATAFORM_POS 4 +#define HASH_CR_DATAFORM_MASK (BIT(4) | BIT(5)) -#define HASH_CR_DMAE_POS 3 -#define HASH_CR_DMAE_MASK MASK_BIT3 +#define HASH_CR_DMAE_POS 3 +#define HASH_CR_DMAE_MASK BIT(3) -#define HASH_CR_INIT_POS 2 -#define HASH_CR_INIT_MASK MASK_BIT2 +#define HASH_CR_INIT_POS 2 +#define HASH_CR_INIT_MASK BIT(2) -#define HASH_CR_PRIVN_POS 1 -#define HASH_CR_PRIVN_MASK MASK_BIT1 +#define HASH_CR_PRIVN_POS 1 +#define HASH_CR_PRIVN_MASK BIT(1) -#define HASH_CR_SECN_POS 0 -#define HASH_CR_SECN_MASK MASK_BIT0 +#define HASH_CR_SECN_POS 0 +#define HASH_CR_SECN_MASK BIT(0) /* Start register bitfields */ -#define HASH_STR_DCAL_POS 8 -#define HASH_STR_DCAL_MASK MASK_BIT8 +#define HASH_STR_DCAL_POS 8 +#define HASH_STR_DCAL_MASK BIT(8) +#define HASH_STR_DEFAULT 0x0 -#define HASH_STR_NBLW_POS 0 -#define HASH_STR_NBLW_MASK 0x0000001FUL +#define HASH_STR_NBLW_POS 0 +#define HASH_STR_NBLW_MASK 0x0000001FUL -#define HASH_NBLW_MAX_VAL 0x1F +#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( \ - sys_ctx_g.registry[HASH_DEVICE_ID_1]->din, (val)) - -#define HASH_INITIALIZE \ - HCL_WRITE_BITS( \ - sys_ctx_g.registry[HASH_DEVICE_ID_1]->cr, \ - 0x01 << HASH_CR_INIT_POS, \ +#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_BITS(reg_name, mask) \ + writel((readl(reg_name) | mask), reg_name) + +#define HASH_CLEAR_BITS(reg_name, mask) \ + writel((readl(reg_name) & ~mask), reg_name) + +#define HASH_PUT_BITS(reg, val, shift, mask) \ + writel(((readl(reg) & ~(mask)) | \ + (((u32)val << shift) & (mask))), reg) + +#define HASH_SET_DIN(val) writel((val), &device_data->base->din) + +#define HASH_INITIALIZE \ + HASH_PUT_BITS( \ + &device_data->base->cr, \ + 0x01, HASH_CR_INIT_POS, \ HASH_CR_INIT_MASK) -#define HASH_SET_DATA_FORMAT(data_format) \ - HCL_WRITE_BITS( \ - sys_ctx_g.registry[HASH_DEVICE_ID_1]->cr, \ - (u32) (data_format) << HASH_CR_DATAFORM_POS, \ +#define HASH_SET_DATA_FORMAT(data_format) \ + HASH_PUT_BITS( \ + &device_data->base->cr, \ + (u32) (data_format), HASH_CR_DATAFORM_POS, \ HASH_CR_DATAFORM_MASK) - -#define HASH_GET_HX(pos) \ - HCL_READ_REG(sys_ctx_g.registry[HASH_DEVICE_ID_1]->hx[pos]) - -#define HASH_SET_NBLW(val) \ - HCL_WRITE_BITS( \ - sys_ctx_g.registry[HASH_DEVICE_ID_1]->str, \ - (u32) (val) << HASH_STR_NBLW_POS, \ +#define HASH_SET_NBLW(val) \ + HASH_PUT_BITS( \ + &device_data->base->str, \ + (u32) (val), HASH_STR_NBLW_POS, \ HASH_STR_NBLW_MASK) - -#define HASH_SET_DCAL \ - HCL_WRITE_BITS( \ - sys_ctx_g.registry[HASH_DEVICE_ID_1]->str, \ - 0x01 << HASH_STR_DCAL_POS, \ +#define HASH_SET_DCAL \ + HASH_PUT_BITS( \ + &device_data->base->str, \ + 0x01, HASH_STR_DCAL_POS, \ HASH_STR_DCAL_MASK) -#define HASH_BLOCK_BYTE_SIZE 64 - /** * struct uint64 - Structure to handle 64 bits integers. - * @high_word: Most significant bits - * @low_word: Least significant bits + * @high_word: Most significant bits. + * @low_word: Least significant bits. * * Used to handle 64 bits integers. */ @@ -146,27 +143,27 @@ struct uint64 { /** * 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) + * @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 @@ -205,16 +202,16 @@ struct hash_register { /** * 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) + * @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 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 @@ -225,36 +222,16 @@ struct hash_register { * 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. We know that this gives a - * checkpatch warning and in the current design it needs to be a - * volatile. We will change it when we will rewrite the driver similar - * to how we have done in cryp-part. We have also read - * Documentation/volatile-considered-harmful.txt as checkpatch tell - * us to do. - */ - volatile struct hash_register *registry[MAX_HASH_DEVICE]; - - /* State of HASH device */ - struct hash_state state[MAX_HASH_DEVICE]; + 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; }; /** @@ -269,42 +246,32 @@ enum hash_device_id { /** * 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 + * @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 -}; - -/** - * 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; + HASH_DATA_32_BITS = 0x0, + HASH_DATA_16_BITS = 0x1, + HASH_DATA_8_BITS = 0x2, + HASH_DATA_1_BIT = 0x3 }; /** - * enum hash_algo - Enumeration for selecting between SHA1 or SHA2 algorithm + * 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 + HASH_ALGO_SHA1 = 0x0, + HASH_ALGO_SHA256 = 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 - 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, @@ -312,10 +279,10 @@ enum hash_op { }; /** - * 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 + * 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. */ struct hash_config { int data_format; @@ -324,48 +291,67 @@ struct hash_config { }; /** - * enum hash_rv - Return values / error codes for hash. + * 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. + * @digestsize The size of current digest. + * @device Pointer to the device structure. */ -enum hash_rv { - HASH_OK = 0, - HASH_MSG_LENGTH_OVERFLOW, - HASH_INVALID_PARAMETER, - HASH_UNSUPPORTED_HW +struct hash_ctx { + u8 key[HASH_BLOCK_SIZE]; + u32 keylen; + u8 updated; + struct hash_state state; + struct hash_config config; + int digestsize; + struct hash_device_data *device; }; /** - * 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_device_data - structure for a hash device. + * @base: Pointer to the hardware base address. + * @list_node: For inclusion in klist. + * @dev: Pointer to the device dev structure. + * @ctx_lock: Spinlock for current_ctx. + * @current_ctx: Pointer to the currently allocated context. + * @power_state: TRUE = power state on, FALSE = power state off. + * @power_state_lock: Spinlock for power_state. + * @regulator: Pointer to the device's power control. + * @clk: Pointer to the device's clock control. + * @restore_dev_state: TRUE = saved state, FALSE = no saved state. */ -struct hash_ctx { - u8 key[HASH_BLOCK_BYTE_SIZE]; - u32 keylen; - u8 updated; - struct hash_state state; - struct hash_config config; +struct hash_device_data { + struct hash_register __iomem *base; + struct klist_node list_node; + struct device *dev; + struct spinlock ctx_lock; + struct hash_ctx *current_ctx; + bool power_state; + struct spinlock power_state_lock; + struct ux500_regulator *regulator; + struct clk *clk; + bool restore_dev_state; }; -int hash_init_base_address(int hash_device_id, t_logical_address base_address); - -int hash_setconfiguration(int hash_device_id, struct hash_config *p_config); +int hash_check_hw(struct hash_device_data *device_data); -void hash_begin(struct hash_ctx *ctx); +int hash_setconfiguration(struct hash_device_data *device_data, + struct hash_config *config); -void hash_get_digest(int hid, u8 *digest, int algorithm); +void hash_begin(struct hash_device_data *device_data, struct hash_ctx *ctx); -int hash_hw_update(struct shash_desc *desc, - int hash_device_id, - const u8 *p_data_buffer, - u32 msg_length); +void hash_get_digest(struct hash_device_data *device_data, + u8 *digest, int algorithm); -int hash_end(struct hash_ctx *ctx, u8 digest[HASH_MSG_DIGEST_SIZE]); +int hash_hw_update(struct ahash_request *req); -int hash_save_state(int hash_device_id, struct hash_state *state); +int hash_save_state(struct hash_device_data *device_data, + struct hash_state *state); -int hash_resume_state(int hash_device_id, const struct hash_state *state); +int hash_resume_state(struct hash_device_data *device_data, + const struct hash_state *state); #endif diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index a2e4ebd8ac1..3b472d0bfaa 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -15,129 +15,124 @@ #include #include #include +#include #include #include -#include #include #include -#include + +#include #include #include #include +#include +#include #include #include "hash_alg.h" -#define DRIVER_NAME "DRIVER HASH" -/* Enable/Disables debug msgs */ -#define DRIVER_DEBUG 1 -#define DRIVER_DEBUG_PFX DRIVER_NAME -#define DRIVER_DBG KERN_DEBUG - -#define MAX_HASH_DIGEST_BYTE_SIZE 32 - -static struct mutex hash_hw_acc_mutex; - -static int debug; -static struct hash_system_context sys_ctx_g; -static struct hash_driver_data *internal_drv_data; +#define DEV_DBG_NAME "hashX hashX:" /** - * struct hash_driver_data - IO Base and clock. - * @base: The IO base for the block. - * @clk: The clock. - * @regulator: The current regulator. - * @power_state: TRUE = power state on, FALSE = power state off. - * @power_state_mutex: Mutex for power_state. - * @restore_dev_ctx: TRUE = saved ctx, FALSE = no saved ctx. + * struct hash_driver_data - data specific to the driver. + * + * @device_list: A list of registered devices to choose from. + * @device_allocation: A semaphore initialized with number of devices. */ struct hash_driver_data { - void __iomem *base; - struct device *dev; - struct clk *clk; - struct regulator *regulator; - bool power_state; - struct mutex power_state_mutex; - bool restore_dev_state; + struct klist device_list; + struct semaphore device_allocation; }; -/* Declaration of functions */ -static void hash_messagepad(int hid, const u32 *message, u8 index_bytes); +static struct hash_driver_data driver_data; +/* Declaration of functions */ /** - * clear_reg_str - Clear the registry hash_str. - * @hid: Hardware device ID + * hash_messagepad - Pads a message and write the nblw bits. + * @device_data: Structure for the hash device. + * @message: Last word of a message + * @index_bytes: The number of bytes in the last message * - * This function will clear the dcal bit and the nblw bits. + * 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 inline void clear_reg_str(int hid) -{ - /* - * We will only clear NBLW since writing 0 to DCAL is done by the - * hardware - */ - sys_ctx_g.registry[hid]->str &= ~HASH_STR_NBLW_MASK; -} +static void hash_messagepad(struct hash_device_data *device_data, + const u32 *message, u8 index_bytes); +/** + * hash_disable_power - Request to disable power and clock. + * @device_data: Structure for the hash device. + * @save_device_state: If true, saves the current hw state. + * + * This function request for disabling power (regulator) and clock, + * and could also save current hw state. + */ static int hash_disable_power( - struct device *dev, - struct hash_driver_data *device_data, - bool save_device_state) + struct hash_device_data *device_data, + bool save_device_state) { int ret = 0; + struct device *dev = device_data->dev; dev_dbg(dev, "[%s]", __func__); - mutex_lock(&device_data->power_state_mutex); + spin_lock(&device_data->power_state_lock); if (!device_data->power_state) goto out; - if (save_device_state) { - hash_save_state(HASH_DEVICE_ID_1, - &sys_ctx_g.state[HASH_DEVICE_ID_1]); + if (save_device_state && device_data->current_ctx) { + hash_save_state(device_data, + &device_data->current_ctx->state); device_data->restore_dev_state = true; } clk_disable(device_data->clk); - ret = regulator_disable(device_data->regulator); + ret = ux500_regulator_atomic_disable(device_data->regulator); if (ret) - dev_err(dev, "[%s]: " - "regulator_disable() failed!", - __func__); + dev_err(dev, "[%s] regulator_disable() failed!", __func__); device_data->power_state = false; out: - mutex_unlock(&device_data->power_state_mutex); + spin_unlock(&device_data->power_state_lock); return ret; } +/** + * hash_enable_power - Request to enable power and clock. + * @device_data: Structure for the hash device. + * @restore_device_state: If true, restores a previous saved hw state. + * + * This function request for enabling power (regulator) and clock, + * and could also restore a previously saved hw state. + */ static int hash_enable_power( - struct device *dev, - struct hash_driver_data *device_data, - bool restore_device_state) + struct hash_device_data *device_data, + bool restore_device_state) { int ret = 0; - + struct device *dev = device_data->dev; dev_dbg(dev, "[%s]", __func__); - mutex_lock(&device_data->power_state_mutex); + spin_lock(&device_data->power_state_lock); if (!device_data->power_state) { - ret = regulator_enable(device_data->regulator); + ret = ux500_regulator_atomic_enable(device_data->regulator); if (ret) { dev_err(dev, "[%s]: regulator_enable() failed!", __func__); goto out; } - ret = clk_enable(device_data->clk); if (ret) { dev_err(dev, "[%s]: clk_enable() failed!", __func__); - regulator_disable(device_data->regulator); + ret = ux500_regulator_atomic_disable( + device_data->regulator); goto out; } device_data->power_state = true; @@ -146,1088 +141,1274 @@ static int hash_enable_power( if (device_data->restore_dev_state) { if (restore_device_state) { device_data->restore_dev_state = false; - hash_resume_state(HASH_DEVICE_ID_1, - &sys_ctx_g.state[HASH_DEVICE_ID_1]); + hash_resume_state(device_data, + &device_data->current_ctx->state); } } out: - mutex_unlock(&device_data->power_state_mutex); + spin_unlock(&device_data->power_state_lock); return ret; } +/** + * hash_get_device_data - Checks for an available hash device and return it. + * @hash_ctx: Structure for the hash context. + * @device_data: Structure for the hash device. + * + * This function check for an available hash device and return it to + * the caller. + * Note! Caller need to release the device, calling up(). + */ +static int hash_get_device_data(struct hash_ctx *ctx, + struct hash_device_data **device_data) +{ + int ret; + struct klist_iter device_iterator; + struct klist_node *device_node; + struct hash_device_data *local_device_data = NULL; + + pr_debug(DEV_DBG_NAME " [%s]", __func__); + + /* Wait until a device is available */ + ret = down_interruptible(&driver_data.device_allocation); + if (ret) + return ret; /* Interrupted */ + + /* Select a device */ + klist_iter_init(&driver_data.device_list, &device_iterator); + device_node = klist_next(&device_iterator); + while (device_node) { + local_device_data = container_of(device_node, + struct hash_device_data, list_node); + spin_lock(&local_device_data->ctx_lock); + /* current_ctx allocates a device, NULL = unallocated */ + if (local_device_data->current_ctx) { + device_node = klist_next(&device_iterator); + } else { + local_device_data->current_ctx = ctx; + ctx->device = local_device_data; + spin_unlock(&local_device_data->ctx_lock); + break; + } + spin_unlock(&local_device_data->ctx_lock); + } + klist_iter_exit(&device_iterator); + + if (!device_node) { + /** + * No free device found. + * Since we allocated a device with down_interruptible, this + * should not be able to happen. + * Number of available devices, which are contained in + * device_allocation, is therefore decremented by not doing + * an up(device_allocation). + */ + return -EBUSY; + } + + *device_data = local_device_data; + + return 0; +} + /** * init_hash_hw - Initialise the hash hardware for a new calculation. - * @desc: The hash descriptor for the job + * @device_data: Structure for the hash device. + * @req: The hash request 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) +static int init_hash_hw(struct hash_device_data *device_data, + struct ahash_request *req) { int ret = 0; - int hash_rv; - struct hash_ctx *ctx = shash_desc_ctx(desc); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); - pr_debug("[init_hash_hw] (ctx=0x%x)!", (u32)ctx); + dev_dbg(device_data->dev, "[%s] (ctx=0x%x)!", __func__, (u32)ctx); - hash_rv = hash_setconfiguration(HASH_DEVICE_ID_1, &ctx->config); - if (hash_rv != HASH_OK) { - pr_err("hash_setconfiguration() failed!"); - ret = -EPERM; + ret = hash_setconfiguration(device_data, &ctx->config); + if (ret) { + dev_err(device_data->dev, "[%s] hash_setconfiguration() " + "failed!", __func__); return ret; } - hash_begin(ctx); + hash_begin(device_data, ctx); return ret; } /** * hash_init - Common hash init function for SHA1/SHA2 (SHA256). - * @desc: The hash descriptor for the job + * @req: The hash request for the job. * * Initialize structures. */ -static int hash_init(struct shash_desc *desc) +static int hash_init(struct ahash_request *req) { - struct hash_ctx *ctx = shash_desc_ctx(desc); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); - pr_debug("[hash_init]: (ctx=0x%x)!", (u32)ctx); + pr_debug(DEV_DBG_NAME "[%s] (ctx=0x%x)!", __func__, (u32)ctx); 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 + * hash_processblock - This function processes a single block of 512 bits (64 + * bytes), word aligned, starting at message. + * @device_data: Structure for the hash device. + * @message: Block (512 bits) of message to be written to + * the HASH hardware. + * + * Reentrancy: Non Re-entrant. */ -static int hash_update(struct shash_desc *desc, const u8 *data, - unsigned int len) +static void hash_processblock( + struct hash_device_data *device_data, + const u32 *message) { - int ret = 0; - int hash_rv = HASH_OK; - - pr_debug("[hash_update]: (data=0x%x, len=%d)!", - (u32)data, len); + u32 count; - mutex_lock(&hash_hw_acc_mutex); + /* + * NBLW bits. Reset the number of bits in last word (NBLW). + */ + HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK); - /* NOTE: The length of the message is in the form of number of bits */ - hash_rv = hash_hw_update(desc, HASH_DEVICE_ID_1, data, len * 8); - if (hash_rv != HASH_OK) { - pr_err("hash_hw_update() failed!"); - ret = -EPERM; - goto out; + /* + * Write message data to the HASH_DIN register. + */ + 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; } - -out: - mutex_unlock(&hash_hw_acc_mutex); - 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 + * hash_messagepad - Pads a message and write the nblw bits. + * @device_data: Structure for the hash device. + * @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 int hash_final(struct shash_desc *desc, u8 *out) +static void hash_messagepad(struct hash_device_data *device_data, + const u32 *message, u8 index_bytes) { - int ret = 0; - int hash_rv = HASH_OK; - struct hash_ctx *ctx = shash_desc_ctx(desc); - struct hash_driver_data *device_data = internal_drv_data; - - int digestsize = crypto_shash_digestsize(desc->tfm); - u8 digest[HASH_MSG_DIGEST_SIZE]; - - pr_debug("[hash_final]: (ctx=0x%x)!", (u32) ctx); - - mutex_lock(&hash_hw_acc_mutex); - - /* Enable device power (and clock) */ - ret = hash_enable_power(device_data->dev, device_data, false); - if (ret) { - dev_err(device_data->dev, "[%s]: " - "hash_enable_power() failed!", __func__); - goto out; - } - - if (!ctx->updated) { - ret = init_hash_hw(desc); - if (ret) { - pr_err("init_hash_hw() failed!"); - goto out_power; - } - } else { - hash_rv = hash_resume_state(HASH_DEVICE_ID_1, &ctx->state); + dev_dbg(device_data->dev, "[%s] (bytes in final msg=%d))", + __func__, index_bytes); + /* + * Clear hash str register, only clear NBLW + * since DCAL will be reset by hardware. + */ + writel((readl(&device_data->base->str) & ~HASH_STR_NBLW_MASK), + &device_data->base->str); - if (hash_rv != HASH_OK) { - pr_err("hash_resume_state() failed!"); - ret = -EPERM; - goto out_power; - } + /* Main loop */ + while (index_bytes >= 4) { + HASH_SET_DIN(message[0]); + index_bytes -= 4; + message++; } - hash_messagepad(HASH_DEVICE_ID_1, ctx->state.buffer, - ctx->state.index); - - hash_get_digest(HASH_DEVICE_ID_1, digest, ctx->config.algorithm); - - memcpy(out, digest, digestsize); + if (index_bytes) + HASH_SET_DIN(message[0]); -out_power: - /* Disable power (and clock) */ - if (hash_disable_power(device_data->dev, device_data, false)) - dev_err(device_data->dev, "[%s]: " - "hash_disable_power() failed!", __func__); + while (device_data->base->str & HASH_STR_DCAL_MASK) + cpu_relax(); -out: - mutex_unlock(&hash_hw_acc_mutex); + /* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */ + HASH_SET_NBLW(index_bytes * 8); + dev_dbg(device_data->dev, "[%s] DIN=0x%08x NBLW=%d", __func__, + readl(&device_data->base->din), + readl(&device_data->base->str)); + HASH_SET_DCAL; + dev_dbg(device_data->dev, "[%s] after dcal -> DIN=0x%08x NBLW=%d", + __func__, readl(&device_data->base->din), + readl(&device_data->base->str)); - return ret; + while (device_data->base->str & HASH_STR_DCAL_MASK) + cpu_relax(); } /** - * sha1_init - SHA1 init function. - * @desc: The hash descriptor for the job + * hash_incrementlength - Increments the length of the current message. + * @ctx: Hash context + * @incr: Length of message processed already + * + * Overflow cannot occur, because conditions for overflow are checked in + * hash_hw_update. */ -static int sha1_init(struct shash_desc *desc) +static void hash_incrementlength(struct hash_ctx *ctx, u32 incr) { - struct hash_ctx *ctx = shash_desc_ctx(desc); - - pr_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; + ctx->state.length.low_word += incr; - return hash_init(desc); + /* Check for wrap-around */ + if (ctx->state.length.low_word < incr) + ctx->state.length.high_word++; } /** - * sha256_init - SHA2 (SHA256) init function. - * @desc: The hash descriptor for the job + * hash_setconfiguration - Sets the required configuration for the hash + * hardware. + * @device_data: Structure for the hash device. + * @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. */ -static int sha256_init(struct shash_desc *desc) +int hash_setconfiguration(struct hash_device_data *device_data, + struct hash_config *config) { - struct hash_ctx *ctx = shash_desc_ctx(desc); + int ret = 0; + dev_dbg(device_data->dev, "[%s] ", __func__); - pr_debug("[sha256_init]: (ctx=0x%x)!", (u32) ctx); + if (config->algorithm != HASH_ALGO_SHA1 && + config->algorithm != HASH_ALGO_SHA256) + return -EPERM; - ctx->config.data_format = HASH_DATA_8_BITS; - ctx->config.algorithm = HASH_ALGO_SHA2; - ctx->config.oper_mode = HASH_OPER_MODE_HASH; + /* + * DATAFORM bits. Set the DATAFORM bits to 0b11, which means the data + * to be written to HASH_DIN is considered as 32 bits. + */ + HASH_SET_DATA_FORMAT(config->data_format); - return hash_init(desc); -} + /* + * Empty message bit. This bit is needed when the hash input data + * contain the empty message. Always set in current impl. but with + * no impact on data different than empty message. + */ + HASH_SET_BITS(&device_data->base->cr, HASH_CR_EMPTYMSG_MASK); -static int hash_export(struct shash_desc *desc, void *out) -{ - struct hash_ctx *ctx = shash_desc_ctx(desc); + /* + * ALGO bit. Set to 0b1 for SHA-1 and 0b0 for SHA-256 + */ + switch (config->algorithm) { + case HASH_ALGO_SHA1: + HASH_SET_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK); + break; - pr_debug("[hash_export]: (ctx=0x%X) (out=0x%X)", - (u32) ctx, (u32) out); - memcpy(out, ctx, sizeof(*ctx)); - return 0; + case HASH_ALGO_SHA256: + HASH_CLEAR_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK); + break; + + default: + dev_err(device_data->dev, "[%s] Incorrect algorithm.", + __func__); + return -EPERM; + } + + /* + * MODE bit. This bit selects between HASH or HMAC mode for the + * selected algorithm. 0b0 = HASH and 0b1 = HMAC. + */ + if (HASH_OPER_MODE_HASH == config->oper_mode) { + HASH_CLEAR_BITS(&device_data->base->cr, + HASH_CR_MODE_MASK); + } else { /* HMAC mode or wrong hash mode */ + ret = -EPERM; + dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!", + __func__); + } + return ret; } -static int hash_import(struct shash_desc *desc, const void *in) +/** + * hash_begin - This routine resets some globals and initializes the hash + * hardware. + * @device_data: Structure for the hash device. + * @ctx: Hash context. + * + * 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. + */ +void hash_begin(struct hash_device_data *device_data, struct hash_ctx *ctx) { - struct hash_ctx *ctx = shash_desc_ctx(desc); + /* HW and SW initializations */ + /* Note: there is no need to initialize buffer and digest members */ + dev_dbg(device_data->dev, "[%s] ", __func__); - pr_debug("[hash_import]: (ctx=0x%x) (in =0x%X)", - (u32) ctx, (u32) in); - memcpy(ctx, in, sizeof(*ctx)); - return 0; -} + while (device_data->base->str & HASH_STR_DCAL_MASK) + cpu_relax(); -static struct shash_alg sha1_alg = { - .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_init, - .update = hash_update, - .final = hash_final, - .export = hash_export, - .import = hash_import, - .descsize = sizeof(struct hash_ctx), - .statesize = sizeof(struct hash_ctx), - .base = { - .cra_name = "sha1", - .cra_driver_name = "sha1-u8500", - .cra_flags = CRYPTO_ALG_TYPE_SHASH, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; + /* + * INIT bit. Set this bit to 0b1 to reset the HASH processor core and + * prepare the initialize the HASH accelerator to compute the message + * digest of a new message. + */ + HASH_INITIALIZE; -static struct shash_alg sha256_alg = { - .digestsize = SHA256_DIGEST_SIZE, - .init = sha256_init, - .update = hash_update, - .final = hash_final, - .export = hash_export, - .import = hash_import, - .descsize = sizeof(struct hash_ctx), - .statesize = sizeof(struct hash_ctx), - .base = { - .cra_name = "sha256", - .cra_driver_name = "sha256-u8500", - .cra_flags = CRYPTO_ALG_TYPE_SHASH, - .cra_blocksize = SHA256_BLOCK_SIZE, - .cra_module = THIS_MODULE, - } -}; + /* + * NBLW bits. Reset the number of bits in last word (NBLW). + */ + HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK); +} /** - * u8500_hash_probe - Function that probes the hash hardware. - * @pdev: The platform device + * hash_hw_update - Updates current HASH computation hashing another part of + * the message. + * @req: Byte array containing the message to be hashed (caller + * allocated). + * + * Reentrancy: Non Re-entrant */ -static int u8500_hash_probe(struct platform_device *pdev) +int hash_hw_update(struct ahash_request *req) { int ret = 0; - int hash_rv = HASH_OK; - struct resource *res = NULL; - struct hash_driver_data *hash_drv_data; + u8 index; + u32 count; + u8 *p_buffer; + struct hash_device_data *device_data; + u8 *p_data_buffer; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); + struct crypto_hash_walk walk; + int msg_length = crypto_hash_walk_first(req, &walk); - pr_debug("[u8500_hash_probe]: (pdev=0x%x)", (u32) pdev); + pr_debug(DEV_DBG_NAME "[%s] ", __func__); - pr_debug("[u8500_hash_probe]: Calling kzalloc()!"); - hash_drv_data = kzalloc(sizeof(struct hash_driver_data), GFP_KERNEL); - if (!hash_drv_data) { - pr_debug("kzalloc() failed!"); - ret = -ENOMEM; - goto out; - } + if (msg_length == 0) + return -EPERM; - hash_drv_data->dev = &pdev->dev; + index = ctx->state.index; + p_buffer = (u8 *)ctx->state.buffer; - pr_debug("[u8500_hash_probe]: Calling platform_get_resource()!"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_debug("platform_get_resource() failed"); - ret = -ENODEV; - goto out_kfree; + /* Check if ctx->state.length + msg_length + overflows */ + if (msg_length > + (ctx->state.length.low_word + msg_length) + && HASH_HIGH_WORD_MAX_VAL == + (ctx->state.length.high_word)) { + dev_err(device_data->dev, "[%s] HASH_MSG_LENGTH_OVERFLOW!", + __func__); + return -EPERM; } - pr_debug("[u8500_hash_probe]: Calling request_mem_region()!"); - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - pr_debug("request_mem_region() failed"); - ret = -EBUSY; - goto out_kfree; + ret = hash_get_device_data(ctx, &device_data); + if (ret) + return ret; + + /* Enable device power (and clock) */ + ret = hash_enable_power(device_data, false); + if (ret) { + dev_err(device_data->dev, "[%s]: " + "hash_enable_power() failed!", __func__); + goto out; } - pr_debug("[u8500_hash_probe]: Calling ioremap()!"); - hash_drv_data->base = ioremap(res->start, resource_size(res)); - if (!hash_drv_data->base) { - pr_err("[u8500_hash] " - "ioremap of hash1 register memory failed!"); - ret = -ENOMEM; - goto out_free_mem; - } - mutex_init(&hash_drv_data->power_state_mutex); - - /* Enable power for HASH hardware block */ - hash_drv_data->regulator = regulator_get(&pdev->dev, "v-ape"); - if (IS_ERR(hash_drv_data->regulator)) { - dev_err(&pdev->dev, "[u8500_hash] " - "could not get hash regulator\n"); - ret = PTR_ERR(hash_drv_data->regulator); - hash_drv_data->regulator = NULL; - goto out_unmap; - } - - pr_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)) { - pr_err("clk_get() failed!"); - ret = PTR_ERR(hash_drv_data->clk); - goto out_regulator; - } - - /* Enable device power (and clock) */ - ret = hash_enable_power(&pdev->dev, hash_drv_data, false); - if (ret) { - dev_err(&pdev->dev, "[%s]: hash_enable_power() failed!", - __func__); - goto out_clk; - } + /* Main loop */ + while (0 != msg_length) { + p_data_buffer = walk.data; + if ((index + msg_length) < HASH_BLOCK_SIZE) { + for (count = 0; count < msg_length; count++) { + p_buffer[index + count] = + *(p_data_buffer + count); + } - pr_debug("[u8500_hash_probe]: Calling hash_init_base_address()->" - "(base=0x%x,DEVICE_ID=%d)!", - (u32) hash_drv_data->base, HASH_DEVICE_ID_1); + index += msg_length; + } else { + if (!ctx->updated) { + ret = init_hash_hw(device_data, req); + if (ret) { + dev_err(device_data->dev, "[%s] " + "init_hash_hw() failed!", + __func__); + goto out; + } + ctx->updated = 1; + } else { + ret = hash_resume_state(device_data, + &ctx->state); + if (ret) { + dev_err(device_data->dev, "[%s] " + "hash_resume_state() failed!", + __func__); + goto out_power; + } + } - /* Setting base address */ - hash_rv = - hash_init_base_address(HASH_DEVICE_ID_1, - (t_logical_address) hash_drv_data->base); - if (hash_rv != HASH_OK) { - pr_err("hash_init_base_address() failed!"); - ret = -EPERM; - goto out_power; - } - pr_debug("[u8500_hash_probe]: Calling mutex_init()!"); - mutex_init(&hash_hw_acc_mutex); + /* + * 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(device_data, + (const u32 *)p_data_buffer); + } else { + for (count = 0; + count < (u32)(HASH_BLOCK_SIZE - index); + count++) { + p_buffer[index + count] = + *(p_data_buffer + count); + } - pr_debug("[u8500_hash_probe]: To register only sha1 and sha256" - " algorithms!"); - internal_drv_data = hash_drv_data; + hash_processblock(device_data, + (const u32 *)p_buffer); + } - ret = crypto_register_shash(&sha1_alg); - if (ret) { - pr_err("Could not register sha1_alg!"); - goto out_power; - } - pr_debug("[u8500_hash_probe]: sha1_alg registered!"); + hash_incrementlength(ctx, HASH_BLOCK_SIZE); + index = 0; - ret = crypto_register_shash(&sha256_alg); - if (ret) { - pr_err("Could not register sha256_alg!"); - goto out_unreg1_tmp; + ret = hash_save_state(device_data, &ctx->state); + if (ret) { + dev_err(device_data->dev, "[%s] " + "hash_save_state() failed!", __func__); + goto out_power; + } + } + msg_length = crypto_hash_walk_done(&walk, 0); } - pr_debug("[u8500_hash_probe]: Calling platform_set_drvdata()!"); - platform_set_drvdata(pdev, hash_drv_data); - - if (hash_disable_power(&pdev->dev, hash_drv_data, false)) - dev_err(&pdev->dev, "[%s]: hash_disable_power()" - " failed!", __func__); - - return 0; - -out_unreg1_tmp: - crypto_unregister_shash(&sha1_alg); + ctx->state.index = index; + dev_dbg(device_data->dev, "[%s] END(msg_length=%d in bits, in=%d, " + "bin=%d))", __func__, msg_length, ctx->state.index, + ctx->state.bit_index); out_power: - hash_disable_power(&pdev->dev, hash_drv_data, false); - -out_clk: - clk_put(hash_drv_data->clk); - -out_regulator: - regulator_put(hash_drv_data->regulator); - -out_unmap: - iounmap(hash_drv_data->base); + /* Disable power (and clock) */ + if (hash_disable_power(device_data, false)) + dev_err(device_data->dev, "[%s]: " + "hash_disable_power() failed!", __func__); +out: + spin_lock(&device_data->ctx_lock); + device_data->current_ctx = NULL; + ctx->device = NULL; + spin_unlock(&device_data->ctx_lock); -out_free_mem: - release_mem_region(res->start, res->end - res->start + 1); + /* + * The down_interruptible part for this semaphore is called in + * cryp_get_device_data. + */ + up(&driver_data.device_allocation); -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 + * hash_resume_state - Function that resumes the state of an calculation. + * @device_data: Pointer to the device structure. + * @device_state: The state to be restored in the hash hardware + * + * Reentrancy: Non Re-entrant */ -static int u8500_hash_remove(struct platform_device *pdev) +int hash_resume_state(struct hash_device_data *device_data, + const struct hash_state *device_state) { - struct resource *res; - struct hash_driver_data *hash_drv_data; - - pr_debug("[u8500_hash_remove]: (pdev=0x%x)", (u32) pdev); + u32 temp_cr; + s32 count; + int hash_mode = HASH_OPER_MODE_HASH; - pr_debug("[u8500_hash_remove]: Calling platform_get_drvdata()!"); - hash_drv_data = platform_get_drvdata(pdev); + dev_dbg(device_data->dev, "[%s] (state(0x%x)))", + __func__, (u32) device_state); - pr_debug("[u8500_hash_remove]: To unregister only sha1 and " - "sha256 algorithms!"); - crypto_unregister_shash(&sha1_alg); - crypto_unregister_shash(&sha256_alg); + if (NULL == device_state) { + dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!", + __func__); + return -EPERM; + } - pr_debug("[u8500_hash_remove]: Calling mutex_destroy()!"); - mutex_destroy(&hash_hw_acc_mutex); + /* Check correctness of index and length members */ + if (device_state->index > HASH_BLOCK_SIZE + || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) { + dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!", + __func__); + return -EPERM; + } - pr_debug("[u8500_hash_remove]: Calling clk_disable()!"); - clk_disable(hash_drv_data->clk); + /* + * INIT bit. Set this bit to 0b1 to reset the HASH processor core and + * prepare the initialize the HASH accelerator to compute the message + * digest of a new message. + */ + HASH_INITIALIZE; - pr_debug("[u8500_hash_remove]: Calling clk_put()!"); - clk_put(hash_drv_data->clk); + temp_cr = device_state->temp_cr; + writel(temp_cr & HASH_CR_RESUME_MASK, &device_data->base->cr); - pr_debug("[u8500_hash_remove]: Calling regulator_disable()!"); - regulator_disable(hash_drv_data->regulator); + if (device_data->base->cr & HASH_CR_MODE_MASK) + hash_mode = HASH_OPER_MODE_HMAC; + else + hash_mode = HASH_OPER_MODE_HASH; - pr_debug("[u8500_hash_remove]: Calling iounmap(): base = 0x%x", - (u32) hash_drv_data->base); - iounmap(hash_drv_data->base); + for (count = 0; count < HASH_CSR_COUNT; count++) { + if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH)) + break; - pr_debug("[u8500_hash_remove]: Calling platform_get_resource()!"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + writel(device_state->csr[count], + &device_data->base->csrx[count]); + } - pr_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); + writel(device_state->csfull, &device_data->base->csfull); + writel(device_state->csdatain, &device_data->base->csdatain); - pr_debug("[u8500_hash_remove]: Calling kfree()!"); - kfree(hash_drv_data); + writel(device_state->str_reg, &device_data->base->str); + writel(temp_cr, &device_data->base->cr); return 0; } -static void u8500_hash_shutdown(struct platform_device *pdev) +/** + * hash_save_state - Function that saves the state of hardware. + * @device_data: Pointer to the device structure. + * @device_state: The strucure where the hardware state should be saved. + * + * Reentrancy: Non Re-entrant + */ +int hash_save_state(struct hash_device_data *device_data, + struct hash_state *device_state) { - struct resource *res = NULL; - struct hash_driver_data *hash_drv_data; + u32 temp_cr; + u32 count; + int hash_mode = HASH_OPER_MODE_HASH; - dev_dbg(&pdev->dev, "[%s]", __func__); + dev_dbg(device_data->dev, "[%s] state(0x%x)))", + __func__, (u32) device_state); - hash_drv_data = platform_get_drvdata(pdev); - if (!hash_drv_data) { - dev_err(&pdev->dev, "[%s]: " - "platform_get_drvdata() failed!", __func__); - return; + if (NULL == device_state) { + dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!", + __func__); + return -EPERM; } - crypto_unregister_shash(&sha1_alg); - crypto_unregister_shash(&sha256_alg); - - mutex_destroy(&hash_hw_acc_mutex); - - iounmap(hash_drv_data->base); + /* Write dummy value to force digest intermediate calculation. This + * actually makes sure that there isn't any ongoing calculation in the + * hardware. + */ + while (device_data->base->str & HASH_STR_DCAL_MASK) + cpu_relax(); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); + temp_cr = readl(&device_data->base->cr); - if (hash_disable_power(&pdev->dev, hash_drv_data, false)) - dev_err(&pdev->dev, "[%s]: " - "hash_disable_power() failed", __func__); + device_state->str_reg = readl(&device_data->base->str); - clk_put(hash_drv_data->clk); - regulator_put(hash_drv_data->regulator); -} + device_state->din_reg = readl(&device_data->base->din); -static int u8500_hash_suspend(struct platform_device *pdev, pm_message_t state) -{ - int ret; - struct hash_driver_data *hash_drv_data; + if (device_data->base->cr & HASH_CR_MODE_MASK) + hash_mode = HASH_OPER_MODE_HMAC; + else + hash_mode = HASH_OPER_MODE_HASH; - dev_dbg(&pdev->dev, "[%s]", __func__); + for (count = 0; count < HASH_CSR_COUNT; count++) { + if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH)) + break; - /* Handle state? */ - hash_drv_data = platform_get_drvdata(pdev); - if (!hash_drv_data) { - dev_err(&pdev->dev, "[%s]: " - "platform_get_drvdata() failed!", __func__); - return -ENOMEM; + device_state->csr[count] = + readl(&device_data->base->csrx[count]); } - ret = hash_disable_power(&pdev->dev, hash_drv_data, true); - if (ret) - dev_err(&pdev->dev, "[%s]: " - "hash_disable_power()", __func__); + device_state->csfull = readl(&device_data->base->csfull); + device_state->csdatain = readl(&device_data->base->csdatain); - return ret; + device_state->temp_cr = temp_cr; + + return 0; } -static int u8500_hash_resume(struct platform_device *pdev) +/** + * hash_check_hw - This routine checks for peripheral Ids and PCell Ids. + * @device_data: + * + */ +int hash_check_hw(struct hash_device_data *device_data) { int ret = 0; - struct hash_driver_data *hash_drv_data; - dev_dbg(&pdev->dev, "[%s]", __func__); + dev_dbg(device_data->dev, "[%s] ", __func__); - hash_drv_data = platform_get_drvdata(pdev); - if (!hash_drv_data) { - dev_err(&pdev->dev, "[%s]: " - "platform_get_drvdata() failed!", __func__); - return -ENOMEM; + if (NULL == device_data) { + ret = -EPERM; + dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!", + __func__); + goto out; } - if (hash_drv_data->restore_dev_state) { - ret = hash_enable_power(&pdev->dev, hash_drv_data, true); - if (ret) - dev_err(&pdev->dev, "[%s]: " - "hash_enable_power() failed!", __func__); + /* Checking Peripheral Ids */ + if ((HASH_P_ID0 == readl(&device_data->base->periphid0)) + && (HASH_P_ID1 == readl(&device_data->base->periphid1)) + && (HASH_P_ID2 == readl(&device_data->base->periphid2)) + && (HASH_P_ID3 == readl(&device_data->base->periphid3)) + && (HASH_CELL_ID0 == readl(&device_data->base->cellid0)) + && (HASH_CELL_ID1 == readl(&device_data->base->cellid1)) + && (HASH_CELL_ID2 == readl(&device_data->base->cellid2)) + && (HASH_CELL_ID3 == readl(&device_data->base->cellid3)) + ) { + ret = 0; + goto out;; + } else { + ret = -EPERM; + dev_err(device_data->dev, "[%s] HASH_UNSUPPORTED_HW!", + __func__); + goto out; } - +out: return ret; } - -static struct platform_driver hash_driver = { - .probe = u8500_hash_probe, - .remove = u8500_hash_remove, - .shutdown = u8500_hash_shutdown, - .suspend = u8500_hash_suspend, - .resume = u8500_hash_resume, - .driver = { - .owner = THIS_MODULE, - .name = "hash1", - }, -}; - /** - * u8500_hash_mod_init - The kernel module init function. + * hash_get_digest - Gets the digest. + * @device_data: Pointer to the device structure. + * @digest: User allocated byte array for the calculated digest. + * @algorithm: The algorithm in use. + * + * 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. */ -static int __init u8500_hash_mod_init(void) +void hash_get_digest(struct hash_device_data *device_data, + u8 *digest, int algorithm) { - pr_debug("u8500_hash_mod_init() is called!"); + u32 temp_hx_val, count; + int loop_ctr; - return platform_driver_register(&hash_driver); -} + if (algorithm != HASH_ALGO_SHA1 && algorithm != HASH_ALGO_SHA256) { + dev_err(device_data->dev, "[%s] Incorrect algorithm %d", + __func__, algorithm); + return; + } -/** - * u8500_hash_mod_fini - The kernel module exit function. - */ -static void __exit u8500_hash_mod_fini(void) -{ - pr_debug("u8500_hash_mod_fini() is called!"); + if (algorithm == HASH_ALGO_SHA1) + loop_ctr = HASH_SHA1_DIGEST_SIZE / sizeof(u32); + else + loop_ctr = HASH_SHA2_DIGEST_SIZE / sizeof(u32); - platform_driver_unregister(&hash_driver); - return; + dev_dbg(device_data->dev, "[%s] digest array:(0x%x)", + __func__, (u32) digest); + + /* Copy result into digest array */ + for (count = 0; count < loop_ctr; count++) { + temp_hx_val = readl(&device_data->base->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); + } } /** - * 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. + * hash_update - The hash update function for SHA1/SHA2 (SHA256). + * @req: The hash request for the job. */ -static void hash_processblock(int hid, const u32 *message) +static int ahash_update(struct ahash_request *req) { - u32 count; + int ret = 0; - clear_bit(HASH_STR_NBLW_MASK, (void *)sys_ctx_g.registry[hid]->str); + pr_debug(DEV_DBG_NAME "[%s] ", __func__); - /* 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; + ret = hash_hw_update(req); + if (ret) { + pr_err(DEV_DBG_NAME "[%s] hash_hw_update() failed!", __func__); + goto out; } + +out: + return ret; } /** - * 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. + * hash_final - The hash final function for SHA1/SHA2 (SHA256). + * @req: The hash request for the job. */ -static void hash_messagepad(int hid, const u32 *message, u8 index_bytes) +static int ahash_final(struct ahash_request *req) { - pr_debug("[u8500_hash_alg] hash_messagepad" - "(bytes in final msg=%d))", index_bytes); + int ret = 0; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); + struct hash_device_data *device_data; + u8 digest[HASH_MSG_DIGEST_SIZE]; - clear_reg_str(hid); + pr_debug(DEV_DBG_NAME "[%s] ", __func__); + ret = hash_get_device_data(ctx, &device_data); + if (ret) + return ret; - /* Main loop */ - while (index_bytes >= 4) { - HASH_SET_DIN(message[0]); - index_bytes -= 4; - message++; + dev_dbg(device_data->dev, "[%s] (ctx=0x%x)!", __func__, (u32) ctx); + + /* Enable device power (and clock) */ + ret = hash_enable_power(device_data, false); + if (ret) { + dev_err(device_data->dev, "[%s]: " + "hash_enable_power() failed!", __func__); + goto out; } - if (index_bytes) - HASH_SET_DIN(message[0]); + if (!ctx->updated) { + ret = init_hash_hw(device_data, req); + if (ret) { + dev_err(device_data->dev, "[%s] init_hash_hw() " + "failed!", __func__); + goto out_power; + } + } else { + ret = hash_resume_state(device_data, &ctx->state); - while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK) - cpu_relax(); + if (ret) { + dev_err(device_data->dev, "[%s] hash_resume_state() " + "failed!", __func__); + goto out_power; + } + } - /* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */ - HASH_SET_NBLW(index_bytes * 8); - pr_debug("[u8500_hash_alg] hash_messagepad -> DIN=0x%08x NBLW=%d", - sys_ctx_g.registry[hid]->din, - sys_ctx_g.registry[hid]->str); - HASH_SET_DCAL; - pr_debug("[u8500_hash_alg] hash_messagepad after dcal -> " - "DIN=0x%08x NBLW=%d", - sys_ctx_g.registry[hid]->din, - sys_ctx_g.registry[hid]->str); + hash_messagepad(device_data, ctx->state.buffer, + ctx->state.index); - while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK) - cpu_relax(); + hash_get_digest(device_data, digest, ctx->config.algorithm); + memcpy(req->result, digest, ctx->digestsize); + +out_power: + /* Disable power (and clock) */ + if (hash_disable_power(device_data, false)) + dev_err(device_data->dev, "[%s] hash_disable_power() failed!", + __func__); + +out: + spin_lock(&device_data->ctx_lock); + device_data->current_ctx = NULL; + ctx->device = NULL; + spin_unlock(&device_data->ctx_lock); + + /* + * The down_interruptible part for this semaphore is called in + * cryp_get_device_data. + */ + up(&driver_data.device_allocation); + + return ret; } -/** - * hash_incrementlength - Increments the length of the current message. - * @ctx: Hash context - * @incr: Length of message processed already - * - * Overflow cannot occur, because conditions for overflow are checked in - * hash_hw_update. - */ -static void hash_incrementlength(struct hash_ctx *ctx, u32 incr) +static int ahash_sha1_init(struct ahash_request *req) { - ctx->state.length.low_word += incr; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); - /* Check for wrap-around */ - if (ctx->state.length.low_word < incr) - ctx->state.length.high_word++; + pr_debug(DEV_DBG_NAME "[%s]: (ctx=0x%x)!", __func__, (u32) ctx); + + ctx->config.data_format = HASH_DATA_8_BITS; + ctx->config.algorithm = HASH_ALGO_SHA1; + ctx->config.oper_mode = HASH_OPER_MODE_HASH; + ctx->digestsize = SHA1_DIGEST_SIZE; + + return hash_init(req); } -/** - * 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) +static int ahash_sha256_init(struct ahash_request *req) { - int hash_rv = HASH_OK; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct hash_ctx *ctx = crypto_ahash_ctx(tfm); - pr_debug("[u8500_hash_alg] hash_setconfiguration())"); + pr_debug(DEV_DBG_NAME "[%s]: (ctx=0x%x)!", __func__, (u32) ctx); - if (p_config->algorithm != HASH_ALGO_SHA1 && - p_config->algorithm != HASH_ALGO_SHA2) - return HASH_INVALID_PARAMETER; + ctx->config.data_format = HASH_DATA_8_BITS; + ctx->config.algorithm = HASH_ALGO_SHA256; + ctx->config.oper_mode = HASH_OPER_MODE_HASH; + ctx->digestsize = SHA256_DIGEST_SIZE; - HASH_SET_DATA_FORMAT(p_config->data_format); + return hash_init(req); +} - HCL_SET_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_EMPTYMSG_MASK); +static int ahash_sha1_digest(struct ahash_request *req) +{ + int ret2, ret1 = ahash_sha1_init(req); - switch (p_config->algorithm) { - case HASH_ALGO_SHA1: - HCL_SET_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_ALGO_MASK); - break; + if (ret1) + goto out; - case HASH_ALGO_SHA2: - HCL_CLEAR_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_ALGO_MASK); - break; + ret1 = ahash_update(req); + ret2 = ahash_final(req); - default: - pr_debug("[u8500_hash_alg] Incorrect algorithm."); - return HASH_INVALID_PARAMETER; +out: + return ret1 ? ret1 : ret2; +} + +static int ahash_sha256_digest(struct ahash_request *req) +{ + int ret2, ret1 = ahash_sha256_init(req); + + if (ret1) + goto out; + + ret1 = ahash_update(req); + ret2 = ahash_final(req); + +out: + return ret1 ? ret1 : ret2; +} + +static struct ahash_alg ahash_sha1_alg = { + .init = ahash_sha1_init, + .update = ahash_update, + .final = ahash_final, + .digest = ahash_sha1_digest, + .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.statesize = sizeof(struct hash_ctx), + .halg.base = { + .cra_name = "sha1", + .cra_driver_name = "sha1-u8500", + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct hash_ctx), + .cra_module = THIS_MODULE, } +}; - /* This bit selects between HASH or HMAC mode for the selected - algorithm */ - if (HASH_OPER_MODE_HASH == p_config->oper_mode) { - HCL_CLEAR_BITS(sys_ctx_g.registry - [hid]->cr, HASH_CR_MODE_MASK); - } else { /* HMAC mode or wrong hash mode */ - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); +static struct ahash_alg ahash_sha256_alg = { + .init = ahash_sha256_init, + .update = ahash_update, + .final = ahash_final, + .digest = ahash_sha256_digest, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct hash_ctx), + .halg.base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-u8500", + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct hash_ctx), + .cra_type = &crypto_ahash_type, + .cra_module = THIS_MODULE, } +}; - return hash_rv; -} +/** + * struct hash_alg *u8500_hash_algs[] - + */ +static struct ahash_alg *u8500_ahash_algs[] = { + &ahash_sha1_alg, + &ahash_sha256_alg +}; /** - * hash_begin - This routine resets some globals and initializes the hash - * hardware. - * @ctx: Hash context - * - * 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. + * hash_algs_register_all - */ -void hash_begin(struct hash_ctx *ctx) +static int ahash_algs_register_all(void) { - /* HW and SW initializations */ - /* Note: there is no need to initialize buffer and digest members */ + int ret; + int i; + int count; - pr_debug("[u8500_hash_alg] hash_begin())"); + pr_debug("[%s]", __func__); - while (sys_ctx_g.registry[HASH_DEVICE_ID_1]->str & HASH_STR_DCAL_MASK) - cpu_relax(); + for (i = 0; i < ARRAY_SIZE(u8500_ahash_algs); i++) { + ret = crypto_register_ahash(u8500_ahash_algs[i]); + if (ret) { + count = i; + pr_err("[%s] alg registration failed", + u8500_ahash_algs[i]->halg.base.cra_driver_name); + goto unreg; + } + } + return 0; +unreg: + for (i = 0; i < count; i++) + crypto_unregister_ahash(u8500_ahash_algs[i]); + return ret; +} - HASH_INITIALIZE; +/** + * hash_algs_unregister_all - + */ +static void ahash_algs_unregister_all(void) +{ + int i; - HCL_CLEAR_BITS(sys_ctx_g.registry[HASH_DEVICE_ID_1]->str, - HASH_STR_NBLW_MASK); + pr_debug(DEV_DBG_NAME " [%s]", __func__); + + for (i = 0; i < ARRAY_SIZE(u8500_ahash_algs); i++) + crypto_unregister_ahash(u8500_ahash_algs[i]); } /** - * 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 + * u8500_hash_probe - Function that probes the hash hardware. + * @pdev: The platform device. */ -int hash_hw_update(struct shash_desc *desc, - int hid, - const u8 *p_data_buffer, - u32 msg_length) +static int u8500_hash_probe(struct platform_device *pdev) { - int hash_rv = HASH_OK; - u8 index; - u8 *p_buffer; - u32 count; - struct hash_ctx *ctx = shash_desc_ctx(desc); - struct hash_driver_data *device_data = internal_drv_data; + int ret = 0; + struct resource *res = NULL; + struct hash_device_data *device_data; + struct device *dev = &pdev->dev; + + dev_dbg(dev, "[%s] (pdev=0x%x)", __func__, (u32) pdev); + device_data = kzalloc(sizeof(struct hash_device_data), GFP_ATOMIC); + if (!device_data) { + dev_dbg(dev, "[%s] kzalloc() failed!", __func__); + ret = -ENOMEM; + goto out; + } - pr_debug("[u8500_hash_alg] hash_hw_update(msg_length=%d / %d), " - "in=%d, bin=%d))", - msg_length, - msg_length / 8, - ctx->state.index, - ctx->state.bit_index); + device_data->dev = dev; + device_data->current_ctx = NULL; - index = ctx->state.index; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(dev, "[%s] platform_get_resource() failed!", __func__); + ret = -ENODEV; + goto out_kfree; + } - p_buffer = (u8 *)ctx->state.buffer; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_dbg(dev, "[%s] request_mem_region() failed!", __func__); + ret = -EBUSY; + goto out_kfree; + } + + device_data->base = ioremap(res->start, resource_size(res)); + if (!device_data->base) { + dev_err(dev, "[%s] ioremap() failed!", + __func__); + ret = -ENOMEM; + goto out_free_mem; + } + spin_lock_init(&device_data->ctx_lock); + spin_lock_init(&device_data->power_state_lock); - /* Number of bytes in the message */ - msg_length /= 8; + /* Enable power for HASH1 hardware block */ + device_data->regulator = ux500_regulator_get(dev); - /* Check parameters */ - if (NULL == p_data_buffer) { - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); - return hash_rv; + if (IS_ERR(device_data->regulator)) { + dev_err(dev, "[%s] regulator_get() failed!", __func__); + ret = PTR_ERR(device_data->regulator); + device_data->regulator = NULL; + goto out_unmap; } - /* Check if ctx->state.length + msg_length - overflows */ - if (msg_length > - (ctx->state.length.low_word + msg_length) - && HASH_HIGH_WORD_MAX_VAL == - (ctx->state.length.high_word)) { - hash_rv = HASH_MSG_LENGTH_OVERFLOW; - pr_err("[u8500_hash_alg] HASH_MSG_LENGTH_OVERFLOW!"); - return hash_rv; + /* Enable the clock for HASH1 hardware block */ + device_data->clk = clk_get(dev, NULL); + if (IS_ERR(device_data->clk)) { + dev_err(dev, "[%s] clk_get() failed!", __func__); + ret = PTR_ERR(device_data->clk); + goto out_regulator; } /* Enable device power (and clock) */ - hash_rv = hash_enable_power(device_data->dev, device_data, false); - if (hash_rv) { - dev_err(device_data->dev, "[%s]: " - "hash_enable_power() failed!", __func__); - goto out; + ret = hash_enable_power(device_data, false); + if (ret) { + dev_err(dev, "[%s]: hash_enable_power() failed!", __func__); + goto out_clk; } - /* Main loop */ - while (0 != msg_length) { - if ((index + msg_length) < HASH_BLOCK_SIZE) { - for (count = 0; count < msg_length; count++) { - p_buffer[index + count] = - *(p_data_buffer + count); - } - - index += msg_length; - msg_length = 0; - } else { - if (!ctx->updated) { - hash_rv = init_hash_hw(desc); - if (hash_rv != HASH_OK) { - pr_err("init_hash_hw() failed!"); - goto out; - } - ctx->updated = 1; - } else { - hash_rv = - hash_resume_state(HASH_DEVICE_ID_1, - &ctx->state); - if (hash_rv != HASH_OK) { - pr_err("hash_resume_state()" - " failed!"); - goto out_power; - } - } - - /* - * 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); - } + ret = hash_check_hw(device_data); + if (ret) { + dev_err(dev, "[%s] hash_check_hw() failed!", __func__); + goto out_power; + } - hash_processblock(hid, (const u32 *)p_buffer); - } + platform_set_drvdata(pdev, device_data); - hash_incrementlength(ctx, HASH_BLOCK_SIZE); - p_data_buffer += (HASH_BLOCK_SIZE - index); - msg_length -= (HASH_BLOCK_SIZE - index); - index = 0; + /* Put the new device into the device list... */ + klist_add_tail(&device_data->list_node, &driver_data.device_list); + /* ... and signal that a new device is available. */ + up(&driver_data.device_allocation); - hash_rv = - hash_save_state(HASH_DEVICE_ID_1, &ctx->state); - if (hash_rv != HASH_OK) { - pr_err("hash_save_state() failed!"); - goto out_power; - } - } + ret = ahash_algs_register_all(); + if (ret) { + dev_err(dev, "[%s] ahash_algs_register_all() " + "failed!", __func__); + goto out_power; } - ctx->state.index = index; + if (hash_disable_power(device_data, false)) + dev_err(dev, "[%s]: hash_disable_power() failed!", __func__); + + dev_info(dev, "[%s] successfully probed", __func__); + return 0; - pr_debug("[u8500_hash_alg] hash_hw_update END(msg_length=%d in " - "bits, in=%d, bin=%d))", - msg_length, - ctx->state.index, - ctx->state.bit_index); out_power: - /* Disable power (and clock) */ - if (hash_disable_power(device_data->dev, device_data, false)) - dev_err(device_data->dev, "[%s]: " - "hash_disable_power() failed!", __func__); + hash_disable_power(device_data, false); + +out_clk: + clk_put(device_data->clk); + +out_regulator: + ux500_regulator_put(device_data->regulator); + +out_unmap: + iounmap(device_data->base); + +out_free_mem: + release_mem_region(res->start, resource_size(res)); + +out_kfree: + kfree(device_data); out: - return hash_rv; + return ret; } /** - * 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 + * u8500_hash_remove - Function that removes the hash device from the platform. + * @pdev: The platform device. */ -int hash_resume_state(int hid, const struct hash_state *device_state) +static int u8500_hash_remove(struct platform_device *pdev) { - u32 temp_cr; - int hash_rv = HASH_OK; - s32 count; - int hash_mode = HASH_OPER_MODE_HASH; + struct resource *res; + struct hash_device_data *device_data; + struct device *dev = &pdev->dev; - pr_debug("[u8500_hash_alg] hash_resume_state(state(0x%x)))", - (u32) device_state); + dev_dbg(dev, "[%s] (pdev=0x%x)", __func__, (u32) pdev); - if (NULL == device_state) { - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); - return hash_rv; + device_data = platform_get_drvdata(pdev); + if (!device_data) { + dev_err(dev, "[%s]: platform_get_drvdata() failed!", + __func__); + return -ENOMEM; } - /* Check correctness of index and length members */ - if (device_state->index > HASH_BLOCK_SIZE - || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) { - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); - return hash_rv; + /* Try to decrease the number of available devices. */ + if (down_trylock(&driver_data.device_allocation)) + return -EBUSY; + + /* Check that the device is free */ + spin_lock(&device_data->ctx_lock); + /* current_ctx allocates a device, NULL = unallocated */ + if (device_data->current_ctx) { + /* The device is busy */ + spin_unlock(&device_data->ctx_lock); + /* Return the device to the pool. */ + up(&driver_data.device_allocation); + return -EBUSY; } - HASH_INITIALIZE; + spin_unlock(&device_data->ctx_lock); - temp_cr = device_state->temp_cr; - sys_ctx_g.registry[hid]->cr = - temp_cr & HASH_CR_RESUME_MASK; + /* Remove the device from the list */ + if (klist_node_attached(&device_data->list_node)) + klist_remove(&device_data->list_node); - if (sys_ctx_g.registry[hid]->cr & HASH_CR_MODE_MASK) - hash_mode = HASH_OPER_MODE_HMAC; - else - hash_mode = HASH_OPER_MODE_HASH; + /* If this was the last device, remove the services */ + if (list_empty(&driver_data.device_list.k_list)) + ahash_algs_unregister_all(); - for (count = 0; count < HASH_CSR_COUNT; count++) { - if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH)) - break; + if (hash_disable_power(device_data, false)) + dev_err(dev, "[%s]: hash_disable_power() failed", + __func__); - sys_ctx_g.registry[hid]->csrx[count] = - device_state->csr[count]; - } + clk_put(device_data->clk); + ux500_regulator_put(device_data->regulator); - sys_ctx_g.registry[hid]->csfull = device_state->csfull; - sys_ctx_g.registry[hid]->csdatain = device_state->csdatain; + iounmap(device_data->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); - sys_ctx_g.registry[hid]->str = device_state->str_reg; - sys_ctx_g.registry[hid]->cr = temp_cr; + kfree(device_data); - return hash_rv; + return 0; } /** - * 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 + * u8500_hash_shutdown - Function that shutdown the hash device. + * @pdev: The platform device */ -int hash_save_state(int hid, struct hash_state *device_state) +static void u8500_hash_shutdown(struct platform_device *pdev) { - u32 temp_cr; - u32 count; - int hash_rv = HASH_OK; - int hash_mode = HASH_OPER_MODE_HASH; + struct resource *res = NULL; + struct hash_device_data *device_data; - pr_debug("[u8500_hash_alg] hash_save_state( state(0x%x)))", - (u32) device_state); + dev_dbg(&pdev->dev, "[%s]", __func__); - if (NULL == device_state) { - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); - return hash_rv; + device_data = platform_get_drvdata(pdev); + if (!device_data) { + dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!", + __func__); + return; } - /* Write dummy value to force digest intermediate calculation. This - * actually makes sure that there isn't any ongoing calculation in the - * hardware. - */ - while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK) - cpu_relax(); - - temp_cr = sys_ctx_g.registry[hid]->cr; - - device_state->str_reg = sys_ctx_g.registry[hid]->str; - - device_state->din_reg = sys_ctx_g.registry[hid]->din; - - if (sys_ctx_g.registry[hid]->cr & HASH_CR_MODE_MASK) - hash_mode = HASH_OPER_MODE_HMAC; - else - hash_mode = HASH_OPER_MODE_HASH; + /* Check that the device is free */ + spin_lock(&device_data->ctx_lock); + /* current_ctx allocates a device, NULL = unallocated */ + if (!device_data->current_ctx) { + if (down_trylock(&driver_data.device_allocation)) + dev_dbg(&pdev->dev, "[%s]: Cryp still in use!" + "Shutting down anyway...", __func__); + /** + * (Allocate the device) + * Need to set this to non-null (dummy) value, + * to avoid usage if context switching. + */ + device_data->current_ctx++; + } + spin_unlock(&device_data->ctx_lock); - for (count = 0; count < HASH_CSR_COUNT; count++) { - if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH)) - break; + /* Remove the device from the list */ + if (klist_node_attached(&device_data->list_node)) + klist_remove(&device_data->list_node); - device_state->csr[count] = - sys_ctx_g.registry[hid]->csrx[count]; - } + /* If this was the last device, remove the services */ + if (list_empty(&driver_data.device_list.k_list)) + ahash_algs_unregister_all(); - device_state->csfull = sys_ctx_g.registry[hid]->csfull; - device_state->csdatain = sys_ctx_g.registry[hid]->csdatain; + iounmap(device_data->base); - device_state->temp_cr = temp_cr; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); - return hash_rv; + if (hash_disable_power(device_data, false)) + dev_err(&pdev->dev, "[%s] hash_disable_power() failed", + __func__); } /** - * 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. + * u8500_hash_suspend - Function that suspends the hash device. + * @pdev: The platform device. + * @state: - */ -int hash_init_base_address(int hid, t_logical_address base_address) +static int u8500_hash_suspend(struct platform_device *pdev, pm_message_t state) { - int hash_rv = HASH_OK; - - pr_debug("[u8500_hash_alg] hash_init_base_address())"); - - if (0 != base_address) { - /* Initializing the registers structure */ - sys_ctx_g.registry[hid] = - (struct hash_register *) base_address; - - /* Checking Peripheral Ids */ - if ((HASH_P_ID0 == sys_ctx_g.registry[hid]->periphid0) - && (HASH_P_ID1 == sys_ctx_g.registry[hid]->periphid1) - && (HASH_P_ID2 == sys_ctx_g.registry[hid]->periphid2) - && (HASH_P_ID3 == sys_ctx_g.registry[hid]->periphid3) - && (HASH_CELL_ID0 == sys_ctx_g.registry[hid]->cellid0) - && (HASH_CELL_ID1 == sys_ctx_g.registry[hid]->cellid1) - && (HASH_CELL_ID2 == sys_ctx_g.registry[hid]->cellid2) - && (HASH_CELL_ID3 == sys_ctx_g.registry[hid]->cellid3) - ) { - hash_rv = HASH_OK; - return hash_rv; - } else { - hash_rv = HASH_UNSUPPORTED_HW; - pr_err("[u8500_hash_alg] HASH_UNSUPPORTED_HW!"); - return hash_rv; - } - } /* end if */ - else { - hash_rv = HASH_INVALID_PARAMETER; - pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!"); - return hash_rv; + int ret; + struct hash_device_data *device_data; + struct hash_ctx *temp_ctx = NULL; + + dev_dbg(&pdev->dev, "[%s]", __func__); + + device_data = platform_get_drvdata(pdev); + if (!device_data) { + dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!", + __func__); + return -ENOMEM; } + + spin_lock(&device_data->ctx_lock); + if (!device_data->current_ctx) + device_data->current_ctx++; + spin_unlock(&device_data->ctx_lock); + + if (device_data->current_ctx == ++temp_ctx) { + if (down_interruptible(&driver_data.device_allocation)) + dev_dbg(&pdev->dev, "[%s]: down_interruptible() " + "failed", __func__); + ret = hash_disable_power(device_data, false); + + } else + ret = hash_disable_power(device_data, true); + + if (ret) + dev_err(&pdev->dev, "[%s]: hash_disable_power()", __func__); + + return ret; } /** - * hash_get_digest - Gets the digest. - * @hid: Hardware device ID - * @digest: User allocated byte array for the calculated digest - * @algorithm: The algorithm in use. - * - * 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. + * u8500_hash_resume - Function that resume the hash device. + * @pdev: The platform device. */ -void hash_get_digest(int hid, u8 *digest, int algorithm) +static int u8500_hash_resume(struct platform_device *pdev) { - u32 temp_hx_val, count; - int loop_ctr; + int ret = 0; + struct hash_device_data *device_data; + struct hash_ctx *temp_ctx = NULL; - if (algorithm != HASH_ALGO_SHA1 && algorithm != HASH_ALGO_SHA2) { - pr_err("[hash_get_digest] Incorrect algorithm %d", algorithm); - return; + dev_dbg(&pdev->dev, "[%s]", __func__); + + device_data = platform_get_drvdata(pdev); + if (!device_data) { + dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!", + __func__); + return -ENOMEM; } - if (algorithm == HASH_ALGO_SHA1) - loop_ctr = HASH_SHA1_DIGEST_SIZE / sizeof(u32); + spin_lock(&device_data->ctx_lock); + if (device_data->current_ctx == ++temp_ctx) + device_data->current_ctx = NULL; + spin_unlock(&device_data->ctx_lock); + + if (!device_data->current_ctx) + up(&driver_data.device_allocation); else - loop_ctr = HASH_SHA2_DIGEST_SIZE / sizeof(u32); + ret = hash_enable_power(device_data, true); - pr_debug("[u8500_hash_alg] hash_get_digest(digest array:(0x%x))", - (u32) digest); + if (ret) + dev_err(&pdev->dev, "[%s]: hash_enable_power() failed!", + __func__); - /* Copy result into digest array */ - for (count = 0; count < loop_ctr; 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 ret; +} + +static struct platform_driver hash_driver = { + .probe = u8500_hash_probe, + .remove = u8500_hash_remove, + .shutdown = u8500_hash_shutdown, + .suspend = u8500_hash_suspend, + .resume = u8500_hash_resume, + .driver = { + .owner = THIS_MODULE, + .name = "hash1", } +}; + +/** + * u8500_hash_mod_init - The kernel module init function. + */ +static int __init u8500_hash_mod_init(void) +{ + pr_debug("[%s] is called!", __func__); + + klist_init(&driver_data.device_list, NULL, NULL); + /* Initialize the semaphore to 0 devices (locked state) */ + sema_init(&driver_data.device_allocation, 0); + + return platform_driver_register(&hash_driver); } +/** + * u8500_hash_mod_fini - The kernel module exit function. + */ +static void __exit u8500_hash_mod_fini(void) +{ + pr_debug("[%s] is called!", __func__); + platform_driver_unregister(&hash_driver); + return; +} module_init(u8500_hash_mod_init); module_exit(u8500_hash_mod_fini); -module_param(debug, int, 0); - MODULE_DESCRIPTION("Driver for ST-Ericsson U8500 HASH engine."); MODULE_LICENSE("GPL"); -- cgit v1.2.3