mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-09-23 07:05:27 +00:00
This is i.MX BSP 5.0.0 release ported to 2.6.31 Signed-off-by: Rob Herring <r.herring@freescale.com> Signed-off-by: Alan Tull <r80115@freescale.com> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
1051 lines
28 KiB
C
1051 lines
28 KiB
C
/*
|
|
* Copyright 2004-2009 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 sah_queue_manager.c
|
|
*
|
|
* @brief This file provides a Queue Manager implementation.
|
|
*
|
|
* The Queue Manager manages additions and removal from the queue and updates
|
|
* the status of queue entries. It also calls sah_HW_* functions to interract
|
|
* with the hardware.
|
|
*/
|
|
|
|
#include "portable_os.h"
|
|
|
|
/* SAHARA Includes */
|
|
#include <sah_driver_common.h>
|
|
#include <sah_queue_manager.h>
|
|
#include <sah_status_manager.h>
|
|
#include <sah_hardware_interface.h>
|
|
#if defined(DIAG_DRV_QUEUE) || defined(DIAG_DRV_STATUS)
|
|
#include <diagnostic.h>
|
|
#endif
|
|
#include <sah_memory_mapper.h>
|
|
|
|
#ifdef DIAG_DRV_STATUS
|
|
|
|
#define FSL_INVALID_RETURN 13
|
|
#define MAX_RETURN_STRING_LEN 22
|
|
#endif
|
|
|
|
/* Defines for parsing value from Error Status register */
|
|
#define SAH_STATUS_MASK 0x07
|
|
#define SAH_ERROR_MASK 0x0F
|
|
#define SAH_CHA_ERR_SOURCE_MASK 0x07
|
|
#define SAH_CHA_ERR_STATUS_MASK 0x0FFF
|
|
#define SAH_DMA_ERR_STATUS_MASK 0x0F
|
|
#define SAH_DMA_ERR_SIZE_MASK 0x03
|
|
#define SAH_DMA_ERR_DIR_MASK 0x01
|
|
|
|
#define SHA_ERROR_STATUS_CONTINUE 0xFFFFFFFF
|
|
#define SHA_CHA_ERROR_STATUS_DONE 0xFFFFFFFF
|
|
|
|
/* this maps the error status register's error source 4 bit field to the API
|
|
* return values. A 0xFFFFFFFF indicates additional fields must be checked to
|
|
* determine an appropriate return value */
|
|
static sah_Execute_Error sah_Execute_Error_Array[] = {
|
|
FSL_RETURN_ERROR_S, /* SAH_ERR_NONE */
|
|
FSL_RETURN_BAD_FLAG_S, /* SAH_ERR_HEADER */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_LENGTH */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_POINTER */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_LENGTH */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_POINTER */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INPUT_BUFFER */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_OUTPUT_BUFFER */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_OUTPUT_BUFFER_STARVATION */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INTERNAL_STATE */
|
|
FSL_RETURN_ERROR_S, /* SAH_ERR_GENERAL_DESCRIPTOR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_RESERVED_FIELDS */
|
|
FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_DESCRIPTOR_ADDRESS */
|
|
FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_LINK_ADDRESS */
|
|
SHA_ERROR_STATUS_CONTINUE, /* SAH_ERR_CHA */
|
|
SHA_ERROR_STATUS_CONTINUE /* SAH_ERR_DMA */
|
|
};
|
|
|
|
static sah_DMA_Error_Status sah_DMA_Error_Status_Array[] = {
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_NO_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_AHB_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_IP_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_PARITY_ERR */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_DMA_BOUNDRY_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_BUSY_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_RESERVED_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S /* SAH_DMA_INT_ERR */
|
|
};
|
|
|
|
static sah_CHA_Error_Status sah_CHA_Error_Status_Array[] = {
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_NO_ERR */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_IP_BUF */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_ADD_ERR */
|
|
FSL_RETURN_BAD_MODE_S, /* SAH_CHA_MODE_ERR */
|
|
FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_DATA_SIZE_ERR */
|
|
FSL_RETURN_BAD_KEY_LENGTH_S, /* SAH_CHA_KEY_SIZE_ERR */
|
|
FSL_RETURN_BAD_MODE_S, /* SAH_CHA_PROC_ERR */
|
|
FSL_RETURN_ERROR_S, /* SAH_CHA_CTX_READ_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_INTERNAL_HW_ERR */
|
|
FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_IP_BUFF_ERR */
|
|
FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_OP_BUFF_ERR */
|
|
FSL_RETURN_BAD_KEY_PARITY_S, /* SAH_CHA_DES_KEY_ERR */
|
|
FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_RES */
|
|
};
|
|
|
|
#ifdef DIAG_DRV_STATUS
|
|
|
|
char sah_return_text[FSL_INVALID_RETURN][MAX_RETURN_STRING_LEN] = {
|
|
"No error", /* FSL_RETURN_OK_S */
|
|
"Error", /* FSL_RETURN_ERROR_S */
|
|
"No resource", /* FSL_RETURN_NO_RESOURCE_S */
|
|
"Bad algorithm", /* FSL_RETURN_BAD_ALGORITHM_S */
|
|
"Bad mode", /* FSL_RETURN_BAD_MODE_S */
|
|
"Bad flag", /* FSL_RETURN_BAD_FLAG_S */
|
|
"Bad key length", /* FSL_RETURN_BAD_KEY_LENGTH_S */
|
|
"Bad key parity", /* FSL_RETURN_BAD_KEY_PARITY_S */
|
|
"Bad data length", /* FSL_RETURN_BAD_DATA_LENGTH_S */
|
|
"Authentication failed", /* FSL_RETURN_AUTH_FAILED_S */
|
|
"Memory error", /* FSL_RETURN_MEMORY_ERROR_S */
|
|
"Internal error", /* FSL_RETURN_INTERNAL_ERROR_S */
|
|
"unknown value", /* default */
|
|
};
|
|
|
|
#endif /* DIAG_DRV_STATUS */
|
|
|
|
/*!
|
|
* This lock must be held while performing any queuing or unqueuing functions,
|
|
* including reading the first pointer on the queue. It also protects reading
|
|
* and writing the Sahara DAR register. It must be held during a read-write
|
|
* operation on the DAR so that the 'test-and-set' is atomic.
|
|
*/
|
|
os_lock_t desc_queue_lock;
|
|
|
|
/*! This is the main queue for the driver. This is shared between all threads
|
|
* and is not protected by mutexes since the kernel is non-preemptable. */
|
|
sah_Queue *main_queue = NULL;
|
|
|
|
/* Internal Prototypes */
|
|
sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state);
|
|
|
|
#ifdef DIAG_DRV_STATUS
|
|
void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address);
|
|
#endif
|
|
|
|
extern wait_queue_head_t *int_queue;
|
|
|
|
/*!
|
|
* This function initialises the Queue Manager
|
|
*
|
|
* @brief Initialise the Queue Manager
|
|
*
|
|
* @return FSL_RETURN_OK_S on success; FSL_RETURN_MEMORY_ERROR_S if not
|
|
*/
|
|
fsl_shw_return_t sah_Queue_Manager_Init(void)
|
|
{
|
|
fsl_shw_return_t ret_val = FSL_RETURN_OK_S;
|
|
|
|
desc_queue_lock = os_lock_alloc_init();
|
|
|
|
if (main_queue == NULL) {
|
|
/* Construct the main queue. */
|
|
main_queue = sah_Queue_Construct();
|
|
|
|
if (main_queue == NULL) {
|
|
ret_val = FSL_RETURN_MEMORY_ERROR_S;
|
|
}
|
|
} else {
|
|
#ifdef DIAG_DRV_QUEUE
|
|
LOG_KDIAG
|
|
("Trying to initialise the queue manager more than once.");
|
|
#endif
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/*!
|
|
* This function closes the Queue Manager
|
|
*
|
|
* @brief Close the Queue Manager
|
|
*
|
|
* @return void
|
|
*/
|
|
void sah_Queue_Manager_Close(void)
|
|
{
|
|
#ifdef DIAG_DRV_QUEUE
|
|
if (main_queue && main_queue->count != 0) {
|
|
LOG_KDIAG
|
|
("Trying to close the main queue when it is not empty.");
|
|
}
|
|
#endif
|
|
|
|
if (main_queue) {
|
|
/* There is no error checking here because there is no way to handle
|
|
it. */
|
|
sah_Queue_Destroy(main_queue);
|
|
main_queue = NULL;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Count the number of entries on the Queue Manager's queue
|
|
*
|
|
* @param ignore_state If non-zero, the @a state parameter is ignored.
|
|
* If zero, only entries matching @a state are counted.
|
|
* @param state State of entry to match for counting.
|
|
*
|
|
* @return Number of entries which matched criteria
|
|
*/
|
|
int sah_Queue_Manager_Count_Entries(int ignore_state, sah_Queue_Status state)
|
|
{
|
|
int count = 0;
|
|
sah_Head_Desc *current_entry;
|
|
|
|
/* Start at the head */
|
|
current_entry = main_queue->head;
|
|
while (current_entry != NULL) {
|
|
if (ignore_state || (current_entry->status == state)) {
|
|
count++;
|
|
}
|
|
/* Jump to the next entry. */
|
|
current_entry = current_entry->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*!
|
|
* This function removes an entry from the Queue Manager's queue. The entry to
|
|
* be removed can be anywhere in the queue.
|
|
*
|
|
* @brief Remove an entry from the Queue Manager's queue.
|
|
*
|
|
* @param entry A pointer to a sah_Head_Desc to remove from the Queue
|
|
* Manager's queue.
|
|
*
|
|
* @pre The #desc_queue_lock must be held before calling this function.
|
|
*
|
|
* @return void
|
|
*/
|
|
void sah_Queue_Manager_Remove_Entry(sah_Head_Desc * entry)
|
|
{
|
|
if (entry == NULL) {
|
|
#ifdef DIAG_DRV_QUEUE
|
|
LOG_KDIAG("NULL pointer input.");
|
|
#endif
|
|
} else {
|
|
sah_Queue_Remove_Any_Entry(main_queue, entry);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* This function appends an entry to the Queue Managers queue. It primes SAHARA
|
|
* if this entry is the first PENDING entry in the Queue Manager's Queue.
|
|
*
|
|
* @brief Appends an entry to the Queue Manager's queue.
|
|
*
|
|
* @param entry A pointer to a sah_Head_Desc to append to the Queue
|
|
* Manager's queue.
|
|
*
|
|
* @pre The #desc_queue_lock may not may be held when calling this function.
|
|
*
|
|
* @return void
|
|
*/
|
|
void sah_Queue_Manager_Append_Entry(sah_Head_Desc * entry)
|
|
{
|
|
sah_Head_Desc *current_entry;
|
|
os_lock_context_t int_flags;
|
|
|
|
#ifdef DIAG_DRV_QUEUE
|
|
if (entry == NULL) {
|
|
LOG_KDIAG("NULL pointer input.");
|
|
}
|
|
#endif
|
|
entry->status = SAH_STATE_PENDING;
|
|
os_lock_save_context(desc_queue_lock, int_flags);
|
|
sah_Queue_Append_Entry(main_queue, entry);
|
|
|
|
/* Prime SAHARA if the operation that was just appended is the only PENDING
|
|
* operation in the queue.
|
|
*/
|
|
current_entry = sah_Find_With_State(SAH_STATE_PENDING);
|
|
if (current_entry != NULL) {
|
|
if (current_entry == entry) {
|
|
sah_Queue_Manager_Prime(entry);
|
|
}
|
|
}
|
|
|
|
os_unlock_restore_context(desc_queue_lock, int_flags);
|
|
}
|
|
|
|
/*!
|
|
* This function marks all entries in the Queue Manager's queue with state
|
|
* SAH_STATE_RESET.
|
|
*
|
|
* @brief Mark all entries with state SAH_STATE_RESET
|
|
*
|
|
* @return void
|
|
*
|
|
* @note This feature needs re-visiting
|
|
*/
|
|
void sah_Queue_Manager_Reset_Entries(void)
|
|
{
|
|
sah_Head_Desc *current_entry = NULL;
|
|
|
|
/* Start at the head */
|
|
current_entry = main_queue->head;
|
|
|
|
while (current_entry != NULL) {
|
|
/* Set the state. */
|
|
current_entry->status = SAH_STATE_RESET;
|
|
/* Jump to the next entry. */
|
|
current_entry = current_entry->next;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* This function primes SAHARA for the first time or after the queue becomes
|
|
* empty. Queue lock must have been set by the caller of this routine.
|
|
*
|
|
* @brief Prime SAHARA.
|
|
*
|
|
* @param entry A pointer to a sah_Head_Desc to Prime SAHARA with.
|
|
*
|
|
* @return void
|
|
*/
|
|
void sah_Queue_Manager_Prime(sah_Head_Desc * entry)
|
|
{
|
|
#ifdef DIAG_DRV_QUEUE
|
|
LOG_KDIAG("Priming SAHARA");
|
|
if (entry == NULL) {
|
|
LOG_KDIAG("Trying to prime SAHARA with a NULL entry pointer.");
|
|
}
|
|
#endif
|
|
|
|
#ifndef SUBMIT_MULTIPLE_DARS
|
|
/* BUG FIX: state machine can transition from Done1 Busy2 directly
|
|
* to Idle. To fix that problem, only one DAR is being allowed on
|
|
* SAHARA at a time */
|
|
if (sah_Find_With_State(SAH_STATE_ON_SAHARA) != NULL) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SAHARA_POWER_MANAGEMENT
|
|
/* check that dynamic power management is not asserted */
|
|
if (!sah_dpm_flag) {
|
|
#endif
|
|
|
|
/* Enable the SAHARA Clocks */
|
|
#ifdef DIAG_DRV_IF
|
|
LOG_KDIAG("SAHARA : Enabling the IPG and AHB clocks\n")
|
|
#endif /*DIAG_DRV_IF */
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
|
|
mxc_clks_enable(SAHARA2_CLK);
|
|
#else
|
|
{
|
|
struct clk *clk = clk_get(NULL, "sahara_clk");
|
|
if (clk != ERR_PTR(ENOENT))
|
|
clk_enable(clk);
|
|
clk_put(clk);
|
|
}
|
|
#endif
|
|
|
|
/* Make sure nothing is in the DAR */
|
|
if (sah_HW_Read_DAR() == 0) {
|
|
#if defined(DIAG_DRV_IF)
|
|
sah_Dump_Chain(&entry->desc, entry->desc.dma_addr);
|
|
#endif /* DIAG_DRV_IF */
|
|
|
|
sah_HW_Write_DAR((entry->desc.dma_addr));
|
|
entry->status = SAH_STATE_ON_SAHARA;
|
|
}
|
|
#ifdef DIAG_DRV_QUEUE
|
|
else {
|
|
LOG_KDIAG("DAR should be empty when Priming SAHARA");
|
|
}
|
|
#endif
|
|
#ifdef SAHARA_POWER_MANAGEMENT
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef SAHARA_POLL_MODE
|
|
|
|
/*!
|
|
* Reset SAHARA, then load the next descriptor on it, if one exists
|
|
*/
|
|
void sah_reset_sahara_request(void)
|
|
{
|
|
sah_Head_Desc *desc;
|
|
os_lock_context_t lock_flags;
|
|
|
|
#ifdef DIAG_DRV_STATUS
|
|
LOG_KDIAG("Sahara required reset from tasklet, replace chip");
|
|
#endif
|
|
sah_HW_Reset();
|
|
|
|
/* Now stick in a waiting request */
|
|
os_lock_save_context(desc_queue_lock, lock_flags);
|
|
if ((desc = sah_Find_With_State(SAH_STATE_PENDING))) {
|
|
sah_Queue_Manager_Prime(desc);
|
|
}
|
|
os_unlock_restore_context(desc_queue_lock, lock_flags);
|
|
}
|
|
|
|
/*!
|
|
* Post-process a descriptor chain after the hardware has finished with it.
|
|
*
|
|
* The status of the descriptor could also be checked. (for FATAL or IGNORED).
|
|
*
|
|
* @param desc_head The finished chain
|
|
* @param error A boolean to mark whether hardware reported error
|
|
*
|
|
* @pre The #desc_queue_lock may not be held when calling this function.
|
|
*/
|
|
void sah_process_finished_request(sah_Head_Desc * desc_head, unsigned error)
|
|
{
|
|
os_lock_context_t lock_flags;
|
|
|
|
if (!error) {
|
|
desc_head->result = FSL_RETURN_OK_S;
|
|
} else if (desc_head->error_status == -1) {
|
|
/* Disaster! Sahara has faulted */
|
|
desc_head->result = FSL_RETURN_ERROR_S;
|
|
} else {
|
|
/* translate from SAHARA error status to fsl_shw return values */
|
|
desc_head->result =
|
|
sah_convert_error_status(desc_head->error_status);
|
|
#ifdef DIAG_DRV_STATUS
|
|
sah_Log_Error(desc_head->current_dar, desc_head->error_status,
|
|
desc_head->fault_address);
|
|
#endif
|
|
}
|
|
|
|
/* Show that the request has been processd */
|
|
desc_head->status = error ? SAH_STATE_FAILED : SAH_STATE_COMPLETE;
|
|
|
|
if (desc_head->uco_flags & FSL_UCO_BLOCKING_MODE) {
|
|
|
|
/* Wake up all processes on Sahara queue */
|
|
wake_up_interruptible(int_queue);
|
|
|
|
} else {
|
|
os_lock_save_context(desc_queue_lock, lock_flags);
|
|
sah_Queue_Append_Entry(&desc_head->user_info->result_pool,
|
|
desc_head);
|
|
os_unlock_restore_context(desc_queue_lock, lock_flags);
|
|
|
|
/* perform callback */
|
|
if (desc_head->uco_flags & FSL_UCO_CALLBACK_MODE) {
|
|
desc_head->user_info->callback(desc_head->user_info);
|
|
}
|
|
}
|
|
} /* sah_process_finished_request */
|
|
|
|
/*! Called from bottom half.
|
|
*
|
|
* @pre The #desc_queue_lock may not be held when calling this function.
|
|
*/
|
|
void sah_postprocess_queue(unsigned long reset_flag)
|
|
{
|
|
|
|
/* if SAHARA needs to be reset, do it here. This starts a descriptor chain
|
|
* if one is ready also */
|
|
if (reset_flag) {
|
|
sah_reset_sahara_request();
|
|
}
|
|
|
|
/* now handle the descriptor chain(s) that has/have completed */
|
|
do {
|
|
sah_Head_Desc *first_entry;
|
|
os_lock_context_t lock_flags;
|
|
|
|
os_lock_save_context(desc_queue_lock, lock_flags);
|
|
|
|
first_entry = main_queue->head;
|
|
if ((first_entry != NULL) &&
|
|
(first_entry->status == SAH_STATE_OFF_SAHARA)) {
|
|
sah_Queue_Remove_Entry(main_queue);
|
|
os_unlock_restore_context(desc_queue_lock, lock_flags);
|
|
|
|
sah_process_finished_request(first_entry,
|
|
(first_entry->
|
|
error_status != 0));
|
|
} else {
|
|
os_unlock_restore_context(desc_queue_lock, lock_flags);
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
return;
|
|
}
|
|
|
|
#endif /* ifndef SAHARA_POLL_MODE */
|
|
|
|
/*!
|
|
* This is a helper function for Queue Manager. This function finds the first
|
|
* entry in the Queue Manager's queue whose state matches the given input
|
|
* state. This function starts at the head of the queue and works towards the
|
|
* tail. If a matching entry was found, the address of the entry is returned.
|
|
*
|
|
* @brief Handle the IDLE state.
|
|
*
|
|
* @param state A sah_Queue_Status value.
|
|
*
|
|
* @pre The #desc_queue_lock must be held before calling this function.
|
|
*
|
|
* @return A pointer to a sah_Head_Desc that matches the given state.
|
|
* @return NULL otherwise.
|
|
*/
|
|
sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state)
|
|
{
|
|
sah_Head_Desc *current_entry = NULL;
|
|
sah_Head_Desc *ret_val = NULL;
|
|
int done_looping = FALSE;
|
|
|
|
/* Start at the head */
|
|
current_entry = main_queue->head;
|
|
|
|
while ((current_entry != NULL) && (done_looping == FALSE)) {
|
|
if (current_entry->status == state) {
|
|
done_looping = TRUE;
|
|
ret_val = current_entry;
|
|
}
|
|
/* Jump to the next entry. */
|
|
current_entry = current_entry->next;
|
|
}
|
|
|
|
return ret_val;
|
|
} /* sah_postprocess_queue */
|
|
|
|
/*!
|
|
* Process the value from the Sahara error status register and convert it into
|
|
* an FSL SHW API error code.
|
|
*
|
|
* Warning, this routine must only be called if an error exists.
|
|
*
|
|
* @param error_status The value from the error status register.
|
|
*
|
|
* @return A return code of type #fsl_shw_return_t.
|
|
*/
|
|
fsl_shw_return_t sah_convert_error_status(uint32_t error_status)
|
|
{
|
|
fsl_shw_return_t ret = FSL_RETURN_ERROR_S; /* catchall */
|
|
uint8_t error_source;
|
|
uint8_t DMA_error_status;
|
|
uint8_t DMA_error_size;
|
|
|
|
/* get the error source from the error status register */
|
|
error_source = error_status & SAH_ERROR_MASK;
|
|
|
|
/* array size is maximum allowed by mask, so no boundary checking is
|
|
* needed here */
|
|
ret = sah_Execute_Error_Array[error_source];
|
|
|
|
/* is this one that needs additional fields checked to determine the
|
|
* error condition? */
|
|
if (ret == SHA_ERROR_STATUS_CONTINUE) {
|
|
/* check the DMA fields */
|
|
if (error_source == SAH_ERR_DMA) {
|
|
/* get the DMA transfer error size. If this indicates that no
|
|
* error was detected, something is seriously wrong */
|
|
DMA_error_size =
|
|
(error_status >> 9) & SAH_DMA_ERR_SIZE_MASK;
|
|
if (DMA_error_size == SAH_DMA_NO_ERR) {
|
|
ret = FSL_RETURN_INTERNAL_ERROR_S;
|
|
} else {
|
|
/* get DMA error status */
|
|
DMA_error_status = (error_status >> 12) &
|
|
SAH_DMA_ERR_STATUS_MASK;
|
|
|
|
/* the DMA error bits cover all the even numbers. By dividing
|
|
* by 2 it can be used as an index into the error array */
|
|
ret =
|
|
sah_DMA_Error_Status_Array[DMA_error_status
|
|
>> 1];
|
|
}
|
|
} else { /* not SAH_ERR_DMA, so must be SAH_ERR_CHA */
|
|
uint16_t CHA_error_status;
|
|
uint8_t CHA_error_source;
|
|
|
|
/* get CHA Error Source. If this indicates that no error was
|
|
* detected, something is seriously wrong */
|
|
CHA_error_source =
|
|
(error_status >> 28) & SAH_CHA_ERR_SOURCE_MASK;
|
|
if (CHA_error_source == SAH_CHA_NO_ERROR) {
|
|
ret = FSL_RETURN_INTERNAL_ERROR_S;
|
|
} else {
|
|
uint32_t mask = 1;
|
|
uint32_t count = 0;
|
|
|
|
/* get CHA Error Status */
|
|
CHA_error_status = (error_status >> 16) &
|
|
SAH_CHA_ERR_STATUS_MASK;
|
|
|
|
/* If more than one bit is set (which shouldn't happen), only
|
|
* the first will be captured */
|
|
if (CHA_error_status != 0) {
|
|
count = 1;
|
|
while (CHA_error_status != mask) {
|
|
++count;
|
|
mask <<= 1;
|
|
}
|
|
}
|
|
|
|
ret = sah_CHA_Error_Status_Array[count];
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
fsl_shw_return_t sah_convert_op_status(uint32_t op_status)
|
|
{
|
|
unsigned op_source = (op_status >> 28) & 0x7;
|
|
unsigned op_detail = op_status & 0x3f;
|
|
fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
|
|
|
|
switch (op_source) {
|
|
case 1: /* SKHA */
|
|
/* Can't this have "ICV" error from CCM ?? */
|
|
break;
|
|
case 2: /* MDHA */
|
|
if (op_detail == 1) {
|
|
ret = FSL_RETURN_AUTH_FAILED_S;
|
|
}
|
|
break;
|
|
case 3: /* RNGA */
|
|
/* Self-test and Compare errors... what to do? */
|
|
break;
|
|
case 4: /* PKHA */
|
|
switch (op_detail) {
|
|
case 0x01:
|
|
ret = FSL_RETURN_PRIME_S;
|
|
break;
|
|
case 0x02:
|
|
ret = FSL_RETURN_NOT_PRIME_S;
|
|
break;
|
|
case 0x04:
|
|
ret = FSL_RETURN_POINT_AT_INFINITY_S;
|
|
break;
|
|
case 0x08:
|
|
ret = FSL_RETURN_POINT_NOT_AT_INFINITY_S;
|
|
break;
|
|
case 0x10:
|
|
ret = FSL_RETURN_GCD_IS_ONE_S;
|
|
break;
|
|
case 0x20:
|
|
ret = FSL_RETURN_GCD_IS_NOT_ONE_S;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifdef DIAG_DRV_STATUS
|
|
|
|
/*!
|
|
* This function logs the diagnostic information for the given error and
|
|
* descriptor address. Only used for diagnostic purposes.
|
|
*
|
|
* @brief (debug only) Log a description of hardware-detected error.
|
|
*
|
|
* @param descriptor The descriptor address that caused the error
|
|
* @param error The SAHARA error code
|
|
* @param fault_address Value from the Fault address register
|
|
*
|
|
* @return void
|
|
*/
|
|
void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address)
|
|
{
|
|
char *source_text; /* verbose error source from register */
|
|
char *address; /* string buffer for descriptor address */
|
|
char *error_log; /* the complete logging message */
|
|
char *cha_log = NULL; /* string buffer for descriptor address */
|
|
char *dma_log = NULL; /* string buffer for descriptor address */
|
|
|
|
uint16_t cha_error = 0;
|
|
uint16_t dma_error = 0;
|
|
|
|
uint8_t error_source;
|
|
sah_Execute_Error return_code;
|
|
|
|
/* log error code and descriptor address */
|
|
error_source = error & SAH_ERROR_MASK;
|
|
return_code = sah_Execute_Error_Array[error_source];
|
|
|
|
source_text = os_alloc_memory(64, GFP_KERNEL);
|
|
|
|
switch (error_source) {
|
|
case SAH_ERR_HEADER:
|
|
sprintf(source_text, "%s", "Header is not valid");
|
|
break;
|
|
|
|
case SAH_ERR_DESC_LENGTH:
|
|
sprintf(source_text, "%s",
|
|
"Descriptor length not equal to sum of link lengths");
|
|
break;
|
|
|
|
case SAH_ERR_DESC_POINTER:
|
|
sprintf(source_text, "%s", "Length or pointer "
|
|
"field is zero while the other is non-zero");
|
|
break;
|
|
|
|
case SAH_ERR_LINK_LENGTH:
|
|
/* note that the Sahara Block Guide 2.7 has an invalid explaination
|
|
* of this. It only happens when a link length is zero */
|
|
sprintf(source_text, "%s", "A data length is a link is zero");
|
|
break;
|
|
|
|
case SAH_ERR_LINK_POINTER:
|
|
sprintf(source_text, "%s",
|
|
"The data pointer in a link is zero");
|
|
break;
|
|
|
|
case SAH_ERR_INPUT_BUFFER:
|
|
sprintf(source_text, "%s", "Input Buffer reported an overflow");
|
|
break;
|
|
|
|
case SAH_ERR_OUTPUT_BUFFER:
|
|
sprintf(source_text, "%s",
|
|
"Output Buffer reported an underflow");
|
|
break;
|
|
|
|
case SAH_ERR_OUTPUT_BUFFER_STARVATION:
|
|
sprintf(source_text, "%s", "Incorrect data in output "
|
|
"buffer after CHA has signalled 'done'");
|
|
break;
|
|
|
|
case SAH_ERR_INTERNAL_STATE:
|
|
sprintf(source_text, "%s", "Internal Hardware Failure");
|
|
break;
|
|
|
|
case SAH_ERR_GENERAL_DESCRIPTOR:
|
|
sprintf(source_text, "%s",
|
|
"Current Descriptor was not legal, but cause is unknown");
|
|
break;
|
|
|
|
case SAH_ERR_RESERVED_FIELDS:
|
|
sprintf(source_text, "%s",
|
|
"Reserved pointer field is non-zero");
|
|
break;
|
|
|
|
case SAH_ERR_DESCRIPTOR_ADDRESS:
|
|
sprintf(source_text, "%s",
|
|
"Descriptor address not word aligned");
|
|
break;
|
|
|
|
case SAH_ERR_LINK_ADDRESS:
|
|
sprintf(source_text, "%s", "Link address not word aligned");
|
|
break;
|
|
|
|
case SAH_ERR_CHA:
|
|
sprintf(source_text, "%s", "CHA Error");
|
|
{
|
|
char *cha_module = os_alloc_memory(5, GFP_KERNEL);
|
|
char *cha_text = os_alloc_memory(45, GFP_KERNEL);
|
|
|
|
cha_error = (error >> 28) & SAH_CHA_ERR_SOURCE_MASK;
|
|
|
|
switch (cha_error) {
|
|
case SAH_CHA_SKHA_ERROR:
|
|
sprintf(cha_module, "%s", "SKHA");
|
|
break;
|
|
|
|
case SAH_CHA_MDHA_ERROR:
|
|
sprintf(cha_module, "%s", "MDHA");
|
|
break;
|
|
|
|
case SAH_CHA_RNG_ERROR:
|
|
sprintf(cha_module, "%s", "RNG ");
|
|
break;
|
|
|
|
case SAH_CHA_PKHA_ERROR:
|
|
sprintf(cha_module, "%s", "PKHA");
|
|
break;
|
|
|
|
case SAH_CHA_NO_ERROR:
|
|
/* can't happen */
|
|
/* no break */
|
|
default:
|
|
sprintf(cha_module, "%s", "????");
|
|
break;
|
|
}
|
|
|
|
cha_error = (error >> 16) & SAH_CHA_ERR_STATUS_MASK;
|
|
|
|
/* Log CHA Error Status */
|
|
switch (cha_error) {
|
|
case SAH_CHA_IP_BUF:
|
|
sprintf(cha_text, "%s",
|
|
"Non-empty input buffer when done");
|
|
break;
|
|
|
|
case SAH_CHA_ADD_ERR:
|
|
sprintf(cha_text, "%s", "Illegal address");
|
|
break;
|
|
|
|
case SAH_CHA_MODE_ERR:
|
|
sprintf(cha_text, "%s", "Illegal mode");
|
|
break;
|
|
|
|
case SAH_CHA_DATA_SIZE_ERR:
|
|
sprintf(cha_text, "%s", "Illegal data size");
|
|
break;
|
|
|
|
case SAH_CHA_KEY_SIZE_ERR:
|
|
sprintf(cha_text, "%s", "Illegal key size");
|
|
break;
|
|
|
|
case SAH_CHA_PROC_ERR:
|
|
sprintf(cha_text, "%s",
|
|
"Mode/Context/Key written during processing");
|
|
break;
|
|
|
|
case SAH_CHA_CTX_READ_ERR:
|
|
sprintf(cha_text, "%s",
|
|
"Context read during processing");
|
|
break;
|
|
|
|
case SAH_CHA_INTERNAL_HW_ERR:
|
|
sprintf(cha_text, "%s", "Internal hardware");
|
|
break;
|
|
|
|
case SAH_CHA_IP_BUFF_ERR:
|
|
sprintf(cha_text, "%s",
|
|
"Input buffer not enabled or underflow");
|
|
break;
|
|
|
|
case SAH_CHA_OP_BUFF_ERR:
|
|
sprintf(cha_text, "%s",
|
|
"Output buffer not enabled or overflow");
|
|
break;
|
|
|
|
case SAH_CHA_DES_KEY_ERR:
|
|
sprintf(cha_text, "%s", "DES key parity error");
|
|
break;
|
|
|
|
case SAH_CHA_RES:
|
|
sprintf(cha_text, "%s", "Reserved");
|
|
break;
|
|
|
|
case SAH_CHA_NO_ERR:
|
|
/* can't happen */
|
|
/* no break */
|
|
default:
|
|
sprintf(cha_text, "%s", "Unknown error");
|
|
break;
|
|
}
|
|
|
|
cha_log = os_alloc_memory(90, GFP_KERNEL);
|
|
sprintf(cha_log,
|
|
" Module %s encountered the error: %s.",
|
|
cha_module, cha_text);
|
|
|
|
os_free_memory(cha_module);
|
|
os_free_memory(cha_text);
|
|
|
|
{
|
|
uint32_t mask = 1;
|
|
uint32_t count = 0;
|
|
|
|
if (cha_error != 0) {
|
|
count = 1;
|
|
while (cha_error != mask) {
|
|
++count;
|
|
mask <<= 1;
|
|
}
|
|
}
|
|
|
|
return_code = sah_CHA_Error_Status_Array[count];
|
|
}
|
|
cha_error = 1;
|
|
}
|
|
break;
|
|
|
|
case SAH_ERR_DMA:
|
|
sprintf(source_text, "%s", "DMA Error");
|
|
{
|
|
char *dma_direction = os_alloc_memory(6, GFP_KERNEL);
|
|
char *dma_size = os_alloc_memory(14, GFP_KERNEL);
|
|
char *dma_text = os_alloc_memory(250, GFP_KERNEL);
|
|
|
|
if ((dma_direction == NULL) || (dma_size == NULL) ||
|
|
(dma_text == NULL)) {
|
|
LOG_KDIAG
|
|
("No memory allocated for DMA debug messages\n");
|
|
}
|
|
|
|
/* log DMA error direction */
|
|
sprintf(dma_direction, "%s",
|
|
(((error >> 8) & SAH_DMA_ERR_DIR_MASK) == 1) ?
|
|
"read" : "write");
|
|
|
|
/* log the size of the DMA transfer error */
|
|
dma_error = (error >> 9) & SAH_DMA_ERR_SIZE_MASK;
|
|
switch (dma_error) {
|
|
case SAH_DMA_SIZE_BYTE:
|
|
sprintf(dma_size, "%s", "byte");
|
|
break;
|
|
|
|
case SAH_DMA_SIZE_HALF_WORD:
|
|
sprintf(dma_size, "%s", "half-word");
|
|
break;
|
|
|
|
case SAH_DMA_SIZE_WORD:
|
|
sprintf(dma_size, "%s", "word");
|
|
break;
|
|
|
|
case SAH_DMA_SIZE_RES:
|
|
sprintf(dma_size, "%s", "reserved size");
|
|
break;
|
|
|
|
default:
|
|
sprintf(dma_size, "%s", "unknown size");
|
|
break;
|
|
}
|
|
|
|
/* log DMA error status */
|
|
dma_error = (error >> 12) & SAH_DMA_ERR_STATUS_MASK;
|
|
switch (dma_error) {
|
|
case SAH_DMA_NO_ERR:
|
|
sprintf(dma_text, "%s", "No DMA Error Code");
|
|
break;
|
|
|
|
case SAH_DMA_AHB_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"AHB terminated a bus cycle with an error");
|
|
break;
|
|
|
|
case SAH_DMA_IP_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"Internal IP bus cycle was terminated with an "
|
|
"error termination. This would likely be "
|
|
"caused by a descriptor length being too "
|
|
"large, and thus accessing an illegal "
|
|
"internal address. Verify the length field "
|
|
"of the current descriptor");
|
|
break;
|
|
|
|
case SAH_DMA_PARITY_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"Parity error detected on DMA command from "
|
|
"Descriptor Decoder. Cause is likely to be "
|
|
"internal hardware fault");
|
|
break;
|
|
|
|
case SAH_DMA_BOUNDRY_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"DMA was requested to cross a 256 byte "
|
|
"internal address boundary. Cause is likely a "
|
|
"descriptor length being too large, thus "
|
|
"accessing two different internal hardware "
|
|
"blocks");
|
|
break;
|
|
|
|
case SAH_DMA_BUSY_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"Descriptor Decoder has made a DMA request "
|
|
"while the DMA controller is busy. Cause is "
|
|
"likely due to hardware fault");
|
|
break;
|
|
|
|
case SAH_DMA_RESERVED_ERR:
|
|
sprintf(dma_text, "%s", "Reserved");
|
|
break;
|
|
|
|
case SAH_DMA_INT_ERR:
|
|
sprintf(dma_text, "%s",
|
|
"Internal DMA hardware error detected. The "
|
|
"DMA controller has detected an internal "
|
|
"condition which should never occur");
|
|
break;
|
|
|
|
default:
|
|
sprintf(dma_text, "%s",
|
|
"Unknown DMA Error Status Code");
|
|
break;
|
|
}
|
|
|
|
return_code =
|
|
sah_DMA_Error_Status_Array[dma_error >> 1];
|
|
dma_error = 1;
|
|
|
|
dma_log = os_alloc_memory(320, GFP_KERNEL);
|
|
sprintf(dma_log,
|
|
" Occurred during a %s operation of a %s transfer: %s.",
|
|
dma_direction, dma_size, dma_text);
|
|
|
|
os_free_memory(dma_direction);
|
|
os_free_memory(dma_size);
|
|
os_free_memory(dma_text);
|
|
}
|
|
break;
|
|
|
|
case SAH_ERR_NONE:
|
|
default:
|
|
sprintf(source_text, "%s", "Unknown Error Code");
|
|
break;
|
|
}
|
|
|
|
address = os_alloc_memory(35, GFP_KERNEL);
|
|
|
|
/* convert error & descriptor address to strings */
|
|
if (dma_error) {
|
|
sprintf(address, "Fault address is 0x%08x", fault_address);
|
|
} else {
|
|
sprintf(address, "Descriptor bus address is 0x%08x",
|
|
descriptor);
|
|
}
|
|
|
|
if (return_code > FSL_INVALID_RETURN) {
|
|
return_code = FSL_INVALID_RETURN;
|
|
}
|
|
|
|
error_log = os_alloc_memory(250, GFP_KERNEL);
|
|
|
|
/* construct final log message */
|
|
sprintf(error_log, "Error source = 0x%08x. Return = %s. %s. %s.",
|
|
error, sah_return_text[return_code], address, source_text);
|
|
|
|
os_free_memory(source_text);
|
|
os_free_memory(address);
|
|
|
|
/* log standard messages */
|
|
LOG_KDIAG(error_log);
|
|
os_free_memory(error_log);
|
|
|
|
/* add additional information if available */
|
|
if (cha_error) {
|
|
LOG_KDIAG(cha_log);
|
|
os_free_memory(cha_log);
|
|
}
|
|
|
|
if (dma_error) {
|
|
LOG_KDIAG(dma_log);
|
|
os_free_memory(dma_log);
|
|
}
|
|
|
|
return;
|
|
} /* sah_Log_Error */
|
|
|
|
#endif /* DIAG_DRV_STATUS */
|
|
|
|
/* End of sah_queue_manager.c */
|