summaryrefslogtreecommitdiff
path: root/drivers/mxc/security/mxc_hacc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/security/mxc_hacc.c')
-rw-r--r--drivers/mxc/security/mxc_hacc.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/drivers/mxc/security/mxc_hacc.c b/drivers/mxc/security/mxc_hacc.c
new file mode 100644
index 000000000000..46829cd791e1
--- /dev/null
+++ b/drivers/mxc/security/mxc_hacc.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_hacc.c
+ *
+ * @brief APIs for HAC Module.
+ *
+ * This file provides the APIs for accessing Hash Acceleration (HAC)
+ * module. HAC module accelerates the creation of a SHA-1 hash over
+ * selected memory spaces. The SHA-1 algorithm is a one-way hash
+ * algorithm that creates 160 bit hash of any length input.
+ *
+ * @ingroup MXC_Security
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#ifdef CONFIG_MXC_HAC_TEST_DEBUG
+#include <linux/module.h>
+#endif /* CONFIG_MXC_HAC_TEST_DEBUG */
+#include "mxc_hacc.h"
+
+/*!
+ * This variable indicates whether HAC module is in suspend or in resume
+ * mode.
+ */
+static unsigned short hac_suspend_state = 0;
+
+/*!
+ * This API configures the start address and block length of the data that
+ * needs to be hashed. Start address indicates starting location from where
+ * the data in the flash memory is to be hashed. The number of blocks that
+ * needs to be hashed is loaded in the block count register. This API does
+ * the starting of Hashing process or continue with Hashing of next block of
+ * data configured in the START_ADDR, BLOCK_COUNT register depending on the
+ * hash parameter passed.
+ *
+ * @param start_address Starting address of the flash memory to be hashed.
+ * user has to pass physical address here.
+ * @param blk_len Number of blocks to be hashed.
+ * @param option Mode of operation like Start or Continue hashing.\n
+ * Following parameters are passed:
+ * HAC_START : Starts the Hashing process.
+ * HAC_LAST_START : Starts the Hashing process with
+ * last block of data.
+ * HAC_CONTINUE : Continue the Hashing process.
+ * HAC_LAST : Continue the Hashing process with
+ * last block of data.
+ *
+ *
+ * @return HAC_SUCCESS Successfully hashed the data.\n
+ * HAC_FAILURE Error in the parameters passed.
+ * HAC_BUSY HAC module is busy in Hashing process.
+ */
+hac_ret hac_hash_data(ulong start_address, ulong blk_len, hac_hash option)
+{
+ struct clk *clk;
+ ulong hac_start, hac_blk_cnt, hac_ctl;
+ hac_ret ret_val = HAC_SUCCESS;
+
+ clk = clk_get(NULL, "hac_clk");
+ clk_enable(clk);
+ hac_start = __raw_readl(HAC_START_ADDR);
+ hac_blk_cnt = __raw_readl(HAC_BLK_CNT);
+ hac_ctl = __raw_readl(HAC_CTL);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return -EPERM;
+ }
+ pr_debug("Function %s. HAC Module: Start address: 0x%08lX, "
+ "block length: 0x%08lX, hash option: 0x%08X\n",
+ __FUNCTION__, start_address, blk_len, option);
+ /* Validating the parameters. Checking for start address to be in
+ 512 bit boundary(64 byte) and block count value must not to be
+ zero. */
+ if ((!start_address) || (blk_len > HAC_MAX_BLOCK_LENGTH) ||
+ (blk_len == 0) || (!((start_address % 64) == 0))) {
+ pr_debug("HAC Module: Invalid parameters passed. \n");
+ return HAC_FAILURE;
+ }
+ if ((hac_ctl & HAC_CTL_BUSY) == 0) {
+ hac_start = start_address;
+ __raw_writel(hac_start, HAC_START_ADDR);
+ hac_blk_cnt = blk_len;
+ __raw_writel(hac_blk_cnt, HAC_BLK_CNT);
+ pr_debug("HAC Module: Hashing start address 0x%08lX\n ",
+ start_address);
+ pr_debug("HAC Module: Hashing blk length 0x%08lX\n ", blk_len);
+ } else {
+ pr_debug("HAC Module: HAC module is busy in Hashing "
+ "process.\n");
+ return HAC_HASH_BUSY;
+ }
+
+ switch (option) {
+ case HAC_START:
+ /*
+ * HAC_START will starts the Hashing of data in the memory.
+ * Before starting the Hashing process, it checks for 'STOP'
+ * bit, 'DONE' bit and 'ERROR' bit is set in the HAC Control
+ * register. If 'STOP' bit is set, it clears the 'STOP' bit in
+ * HAC Control register. If 'DONE' bit and 'ERROR' bit are
+ * set, they are cleared.
+ */
+ pr_debug("HAC Module: Starts the hashing process \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ pr_debug("HAC Module: STOP bit is set while"
+ "starting the Hashing\n");
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit and 'ERROR' bit is been set.
+ If they are set write to clear those bits */
+ if (((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) ||
+ ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR)) {
+ pr_debug("HAC Module: DONE and ERROR bit is set"
+ "while starting the Hashing\n");
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ hac_ctl |= HAC_CTL_ERROR;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ hac_ctl |= HAC_CTL_START;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_START_LAST:
+ /*
+ * HAC_START_LAST will starts the Hashing of last block of data
+ * in the memory. Since this is last block of data in the
+ * memory 'PAD' bit in HAC control register is set to add
+ * appropriate padding to the end of the data structure.
+ * Before starting the Hashing process, it checks for 'STOP'
+ * bit, 'DONE' bit and 'ERROR' bit is set in the HAC Control
+ * register. If 'STOP' bit is set, it clears the 'STOP' bit in
+ * HAC Control register. If 'DONE' bit and 'ERROR' bit are
+ * set, they are cleared.
+ */
+ pr_debug("HAC Module: Starts with last block"
+ "the hashing process \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ pr_debug("HAC Module: STOP bit is set while"
+ "starting the Hashing\n");
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit and 'ERROR' bit is been set.
+ If they are set write to clear those bits */
+ if (((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) ||
+ ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR)) {
+ pr_debug(" HAC Module: DONE and ERROR bit is set"
+ "while starting the Hashing\n");
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ hac_ctl |= HAC_CTL_ERROR;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ hac_ctl |= HAC_CTL_START;
+ __raw_writel(hac_ctl, HAC_CTL);
+ /* Hash for the last block by padding it. */
+ pr_debug("HAC Module: Setting the PAD bit while start"
+ "Hashing the last block\n");
+ hac_ctl |= HAC_CTL_PAD;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_CONTINUE:
+ /*
+ * HAC_CONTINUE will continue the Hashing of data in the memory.
+ * This will continue the hashing processing by taking into
+ * consideration of the previous hash result and continues
+ * further hashing of the new data block. Before continue the
+ * Hashing process, it checks for 'STOP' bit, 'DONE' bit and
+ * 'ERROR' bit is set in the HAC Control register. If 'STOP'
+ * bit is set, it clears the 'STOP' bit in Control register.
+ * If 'DONE' bit is set, it clears the 'DONE' bit in control
+ * register. If 'ERROR' bit is set, then error message is
+ * indicated to the user.
+ */
+ pr_debug("HAC Module: Continue hashing process. \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit is been set. If it is set write
+ one to clear the bit */
+ if ((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) {
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if 'ERROR' bit is been set. If it is set resturn
+ return back indicating error in Hashing porcess. */
+ if ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR) {
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_CONTINUE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_LAST:
+ /*
+ * HAC_LAST will continue the Hashing of last block of data
+ * in the memory. Since this is last block of data in the
+ * memory 'PAD' bit in HAC control register is set to add
+ * appropriate padding to the end of the data structure.
+ * This will continue the hashing processing by taking into
+ * consideration of the previous hash result and continues
+ * further hashing of the new data block. Before continue the
+ * Hashing process, it checks for 'STOP' bit, 'DONE' bit and
+ * 'ERROR' bit is set in the HAC Control register. If 'STOP'
+ * bit is set, it clears the 'STOP' bit in Control register.
+ * If 'DONE' bit is set, it clears the 'DONE' bit in control
+ * register. If 'ERROR' bit is set, then error message is
+ * indicated to the user.
+ */
+ pr_debug("HAC Module: Last block to hash. \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit is been set. If it is set write
+ one to clear the bit */
+ if ((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) {
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if 'ERROR' bit is been set. If it is set resturn
+ return back indicating error in Hashing porcess. */
+ if ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR) {
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_CONTINUE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ /* Continuing the hash for the last block by padding it. */
+ hac_ctl |= HAC_CTL_PAD;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ default:
+ ret_val = HAC_FAILURE;
+ /* NOT RESPONDING */
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API returns the status of the Hashing.
+ *
+ * @return HAC_BUSY : Indicated HAC Module is busy with Hashing.\n
+ * HAC_DONE : Indicates Hashing of data is done.\n
+ * HAC_ERR : Indicates error has occurred during Hashing.\n
+ * HAC_UNKNOWN: Hashing status unknown. This may be when the
+ * hashing process has not been initiated atleast
+ * once or 'ERROR' bit or 'DONE' bits were reset
+ * after the hashing process was completed.
+ */
+hac_hash_status hac_hashing_status(void)
+{
+ ulong hac_ctl;
+ hac_ctl = __raw_readl(HAC_CTL);
+ if ((hac_ctl & HAC_CTL_BUSY) != 0) {
+ pr_debug("HAC Module: Hash module is in busy state \n");
+ return HAC_BUSY;
+ } else if ((hac_ctl & HAC_CTL_DONE) != 0) {
+ /* Clearing the done bit of the control register */
+ pr_debug("HAC Module: Hashing of data is done \n");
+ return HAC_DONE;
+ } else if ((hac_ctl & HAC_CTL_ERROR) != 0) {
+ /* Clearing the error bit of the control register */
+ pr_debug("HAC Module: Error has occurred during hashing \n");
+ return HAC_ERR;
+ } else {
+ return HAC_UNKNOWN;
+ }
+}
+
+/*!
+ * This API returns the status of the Hash module.
+ *
+ * @return Value of the Hashing control register.
+ */
+ulong hac_get_status(void)
+{
+ ulong hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: Hashing status register value 0x%08lX\n ",
+ hac_ctl);
+ return hac_ctl;
+}
+
+/*!
+ * This API stops the Hashing of data when the Hashing is in progress.
+ */
+hac_ret hac_stop(void)
+{
+ ulong hac_ctl;
+ hac_ctl = __raw_readl(HAC_CTL);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ pr_debug("HAC Module: Stop hashing process. \n");
+ hac_ctl |= HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This API reads 160 bit hash result from Hash result register. The data is
+ * copied to the memory pointed by the input pointer.
+ *
+ * @param hash_result_reg structure Pointer where the hash result is
+ * copied.
+ */
+hac_ret hac_hash_result(hac_hash_rlt * hash_result_reg)
+{
+ ulong hac_hsh4, hac_hsh3, hac_hsh2, hac_hsh1, hac_hsh0;
+ struct clk *clk;
+
+ clk = clk_get(NULL, "hac_clk");
+ hac_hsh4 = __raw_readl(HAC_HSH4);
+ hac_hsh3 = __raw_readl(HAC_HSH3);
+ hac_hsh2 = __raw_readl(HAC_HSH2);
+ hac_hsh1 = __raw_readl(HAC_HSH1);
+ hac_hsh0 = __raw_readl(HAC_HSH0);
+ clk_disable(clk);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ pr_debug("HAC Module: Read hash result \n");
+ hash_result_reg->hash_result[0] = hac_hsh4;
+ hash_result_reg->hash_result[1] = hac_hsh3;
+ hash_result_reg->hash_result[2] = hac_hsh2;
+ hash_result_reg->hash_result[3] = hac_hsh1;
+ hash_result_reg->hash_result[4] = hac_hsh0;
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This API will initiates software reset of the entire HAC module. It resets
+ * all state machine to their default values. All status bits (BUSY/ERROR/DONE)
+ * and any pending interrupts are cleared.
+ *
+ * @return HAC_SUCCESS Successfully in doing software reset.\n
+ * HAC_FAILURE Error in doing software reset.
+ */
+hac_ret hac_swrst(void)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Software reset function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_SWRST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ return hac_ret;
+}
+
+/*!
+ * This API configures the burst mode of the HAC. When Burst mode set in HAC
+ * Control register then ARM9 is configured for a 16-WORD burst, while Burst
+ * mode is cleared then ARM9 is configured for a incremental burst.
+ *
+ * @param burst_mode Configures burst mode operations.
+ *
+ * @return HAC_SUCCESS Successfully in configuring burst mode.\n
+ * HAC_FAILURE Error in configuring burst mode.
+ */
+hac_ret hac_burst_mode(hac_burst_mode_config burst_mode)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Burst Mode function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ switch (burst_mode) {
+ case HAC_INR_BURST:
+ hac_ctl |= HAC_CTL_BURST_MODE;
+ break;
+
+ case HAC_16WORD_BURST:
+ hac_ctl &= ~HAC_CTL_BURST_MODE;
+ break;
+
+ default:
+ hac_ret = HAC_FAILURE;
+ break;
+ }
+ return hac_ret;
+}
+
+/*!
+ * This API configures HAC burst read nature.
+ *
+ * @param burst_read Configures burst read.
+ *
+ * @return HAC_SUCCESS Successfully in configuring burst read.\n
+ * HAC_FAILURE Error in configuring burst read.
+ */
+hac_ret hac_burst_read(hac_burst_read_config burst_read)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Burst Read function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ switch (burst_read) {
+ case HAC_16WORD_BURST_READ:
+ __raw_writel(HAC_CTL_16WORD_BURST, HAC_CTL);
+ break;
+
+ case HAC_8WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_8WORD_BURST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_4WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_4WORD_BURST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_NO_WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_NO_BURST_READ;
+ break;
+
+ default:
+ hac_ret = HAC_FAILURE;
+ break;
+ }
+ return hac_ret;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the HAC in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on HAC
+ * to suspend.
+ * @param state the power state the device is entering.
+ *
+ * @return The function always returns HAC_SUCCESS.
+ */
+hac_ret hac_suspend(struct platform_device * pdev, pm_message_t state)
+{
+ ulong hac_ctl;
+ struct clk *clk;
+
+ hac_ctl = __raw_readl(HAC_CTL);
+ clk = clk_get(NULL, "hac_clk");
+ hac_suspend_state = 1;
+
+ pr_debug("HAC Module: In suspend power down.\n");
+
+ /* Enable stop bits in HAC Control Register. */
+ hac_ctl |= HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ clk_disable(clk);
+
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This function is called to bring the HAC back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure used to give information on HAC
+ * to resume.
+ *
+ * @return The function always returns HAC_SUCCESS.
+ */
+hac_ret hac_resume(struct platform_device * pdev)
+{
+ ulong hac_ctl;
+ struct clk *clk;
+
+ clk = clk_get(NULL, "hac_clk");
+ clk_enable(clk);
+ hac_ctl = __raw_readl(HAC_CTL);
+
+ pr_debug("HAC Module: Resume power on.\n");
+ /* Disable stop bit in HAC Control register. */
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+
+ hac_suspend_state = 0;
+
+ return HAC_SUCCESS;
+}
+#else
+#define mxc_hac_suspend NULL
+#define mxc_hac_resume NULL
+#endif /* CONFIG_PM */