/*
     This file is the API to a hardware mutex device for FreeRTOS.
 */

/***************************** Include Files *********************************/

#if 1
/*****************************************************************************
* Filename:          C:\Users\tseng\Desktop\edk_test\ml509_quad_one_plb\drivers/user_mutex_v1_00_a/src/user_mutex.c
* Version:           1.00.a
* Description:       user_mutex (new FSL core) Driver Source File
* Date:              Thu Jul 11 11:23:19 2013 (by Create and Import Peripheral Wizard)
*****************************************************************************/

#include "portmutex.h"

int mutex_lock(int mutex_num, int CPU_ID)
{
    int LockPattern = ((CPU_ID << 1) | 1);
    int value;

    printf("CPU %d mutex request.\r\n", CPU_ID);
    while(1)
    {
        write_into_fsl(0, input_slot_id);//function = lock
        write_into_fsl(mutex_num, input_slot_id);//mutex number
        write_into_fsl(LockPattern, input_slot_id);//pattern
        read_from_fsl(value, output_slot_id);
        if (value == LockPattern)
        {
            break;
        }
    }
    printf("CPU %d mutex enters with value = %d.\r\n", CPU_ID, value);
    return value;
}

int mutex_unlock(int mutex_num, int CPU_ID)
{
    int LockPattern = ((CPU_ID << 1) | 1);
    int value = 1;

    printf("CPU %d mutex request.\r\n", CPU_ID);
    while(1)
    {
        write_into_fsl(1, input_slot_id);//function = unlock
        write_into_fsl(mutex_num, input_slot_id);//mutex number
        write_into_fsl(LockPattern, input_slot_id);//pattern
        read_from_fsl(value, output_slot_id);
        if (value == 0)
        {
            break;
        }
    }
    printf("CPU %d mutex leaves.\r\n", CPU_ID);
    return (value == 0);
}

#else

#include <string.h>
#include "portmutex.h"
#include "xparameters.h"

#define XPAR_MUTEX_0_IF_0_DEVICE_ID 0
#define XPAR_MUTEX_0_IF_0_BASEADDR 0xA0000000
#define XPAR_MUTEX_0_IF_0_NUM_MUTEX 16
#define XPAR_MUTEX_0_IF_0_ENABLE_USER 0

#include "xil_types.h"
#include "xil_assert.h"

/*
* The configuration table for devices
*/

my_Mutex_Config my_Mutex_ConfigTable[] =
{
    {
        XPAR_MUTEX_0_IF_0_DEVICE_ID,
        XPAR_MUTEX_0_IF_0_BASEADDR,
        XPAR_MUTEX_0_IF_0_NUM_MUTEX,
        XPAR_MUTEX_0_IF_0_ENABLE_USER,
    }
    ,
};

/*****************************************************************************
*
* Looks up the device configuration based on the unique device ID. The config
* table contains the configuration info for each device in the system.
*
* @param	DeviceId is the unique device ID to search for in the config
*		table.
*
* @return	A pointer to the configuration that matches the given device ID,
*		or NULL if no match is found.
*
* @note		None.
*
******************************************************************************/
my_Mutex_Config *my_Mutex_LookupConfig(u16 DeviceId)
{
    my_Mutex_Config *CfgPtr = NULL;
    int Index;

    for (Index = 0; Index < XPAR_XMUTEX_NUM_INSTANCES; Index++)
    {
        if (my_Mutex_ConfigTable[Index].DeviceId == DeviceId)
        {
            CfgPtr = &my_Mutex_ConfigTable[Index];
            break;
        }
    }

    return CfgPtr;
}

/*****************************************************************************/
/**
*
* Initializes a specific Mutex instance/driver.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	ConfigPtr is the device configuration structure containing
*		required HW build data.
* @param	EffectiveAddress is the Physical address of the hardware in a
*		Virtual Memory operating system environment.
*		It is the Base Address in a stand alone environment.
*
* @return
*		- XST_SUCCESS if initialization was successful
*
* @note		None.
*
******************************************************************************/
int my_Mutex_CfgInitialize(my_Mutex * InstancePtr, my_Mutex_Config * ConfigPtr,
    u32 EffectiveAddress)
{
    Xil_AssertNonvoid(InstancePtr != NULL);
    Xil_AssertNonvoid(ConfigPtr != NULL);

    /* Clear instance memory and make copy of configuration */
    memset(InstancePtr, 0, sizeof(my_Mutex));
    memcpy(&InstancePtr->Config, ConfigPtr, sizeof(my_Mutex_Config));

    InstancePtr->Config.BaseAddress = EffectiveAddress;
    InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

    return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Locks a particular Mutex lock within a Mutex device. Call blocks till the
* Mutex is locked.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number  with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
*
* @return	None
*
* @note
*		- my_Mutex_Trylock is a blocking call. This call blocks until the
*		  user gets the lock.
*		- Use my_Mutex_Trylock for a Non-Blocking call. The user gets the
*		  lock if it is available and returns immediately if the lock
*		  is not available.
*
******************************************************************************/
void my_Mutex_Lock(my_Mutex * InstancePtr, u8 MutexNumber, unsigned int CPU_ID)
{
    u32 LockPattern = ((CPU_ID << OWNER_SHIFT) | LOCKED_BIT);
    u32 Value;

    Xil_AssertVoid(InstancePtr != NULL);
    Xil_AssertVoid(MutexNumber < InstancePtr->Config.NumMutex);
    asm __volatile__("addi r16, r3, 100\n");
    while (1)
    {
        my_Mutex_WriteReg(InstancePtr->Config.BaseAddress, MutexNumber,
            XMU_MUTEX_REG_OFFSET, LockPattern);
        Value = my_Mutex_ReadReg(InstancePtr->Config.BaseAddress, MutexNumber,
            XMU_MUTEX_REG_OFFSET);
        asm __volatile__("addi r16, r3, 0\n");
        if (Value == LockPattern)
        {
            break;
        }
    }
}

/*****************************************************************************/
/**
*
* Unlocks a particular Mutex lock within a Mutex device.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
*
* @return
*
*		- XST_SUCCESS if locking was successful.
*		- XST_FAILURE if the Mutex was locked by process with
*		  different ID.
*
* @note		None.
*
******************************************************************************/
int my_Mutex_Unlock(my_Mutex * InstancePtr, u8 MutexNumber, unsigned int CPU_ID)
{
    u32 UnLockPattern = ((CPU_ID << OWNER_SHIFT) | 0x0);
    u32 Value;

    Xil_AssertNonvoid(InstancePtr != NULL);
    Xil_AssertNonvoid(MutexNumber < InstancePtr->Config.NumMutex);

    Value = my_Mutex_ReadReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_MUTEX_REG_OFFSET);

    /* Verify that the caller actually owns the Mutex */
    if ((Value & OWNER_MASK) != UnLockPattern)
    {
        return XST_FAILURE;
    }

    my_Mutex_WriteReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_MUTEX_REG_OFFSET, UnLockPattern);

    return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Gets the current lock state of a Mutex lock within a Mutex device.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
*
* @return
*		- TRUE if locked
*		- FALSE if unlocked
*
* @note		None.
*
******************************************************************************/
int my_Mutex_IsLocked(my_Mutex * InstancePtr, u8 MutexNumber)
{
    u32 Value;

    Xil_AssertNonvoid(InstancePtr != NULL);
    Xil_AssertNonvoid(MutexNumber < InstancePtr->Config.NumMutex);

    Value = my_Mutex_ReadReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_MUTEX_REG_OFFSET);

    return ((int) (Value & LOCKED_BIT));
}

/*****************************************************************************/
/**
*
* Gets the current status of a Mutex lock within a Mutex device.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
* @param	Locked is a pointer where the current lock status is stored.
*		Sets memory pointed to by 'Locked' to 1 if the Mutex is locked
*		and 0 if it is unlocked.
* @param	Owner is a pointer where the current owner status is stored.
*.		If the Mutex is locked, the memory pointed to by 'Owner' is
*		updated to reflect the CPU ID that has currently locked this
*		Mutex.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void my_Mutex_GetStatus(my_Mutex * InstancePtr, u8 MutexNumber, u32 * Locked,
    u32 * Owner)
{
    u32 Value;

    Xil_AssertVoid(InstancePtr != NULL);
    Xil_AssertVoid(MutexNumber < InstancePtr->Config.NumMutex);
    Xil_AssertVoid(Locked != NULL);
    Xil_AssertVoid(Owner != NULL);

    Value = my_Mutex_ReadReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_MUTEX_REG_OFFSET);
    *Locked = (Value & LOCKED_BIT);
    *Owner = (Value & OWNER_MASK) >> OWNER_SHIFT;
}

/*****************************************************************************/
/**
*
* Gets the USER register of a Mutex lock within a Mutex device.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
* @param	User is a pointer to an u32 where the current user register
		value is stored by this function.
* @return
*		- XST_SUCCESS if successful. Memory pointed to by User is
*		  updated to reflect the contents of the user register.
*		- XST_NO_FEATURE if the Mutex was not configured with a USER
*		  register.
*
* @note		None.
*
******************************************************************************/
int my_Mutex_GetUser(my_Mutex * InstancePtr, u8 MutexNumber, u32 * User)
{
    Xil_AssertNonvoid(InstancePtr != NULL);
    Xil_AssertNonvoid(MutexNumber < InstancePtr->Config.NumMutex);
    Xil_AssertNonvoid(User != NULL);

    if (!(InstancePtr->Config.UserReg))
    {
        return XST_NO_FEATURE;
    }

    *User = my_Mutex_ReadReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_USER_REG_OFFSET);
    return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Sets the USER register of a Mutex lock within a Mutex device.
*
* @param	InstancePtr is a pointer to the my_Mutex instance to be worked on.
* @param	MutexNumber is the specific Mutex lock within the device to
*		operate on. Each device may contain multiple Mutex locks.
*		The Mutex number is a zero based number with a range of
*		0 - (InstancePtr->Config.NumMutex - 1).
* @param	User is the value to update the USER register with.
*
* @return
*		- XST_SUCCESS if the USER register is written with the
*		  given value .
*		- XST_NO_FEATURE if the Mutex was not configured with a
*		  USER register.
*
* @note		None.

*
******************************************************************************/
int my_Mutex_SetUser(my_Mutex * InstancePtr, u8 MutexNumber, u32 User)
{
    Xil_AssertNonvoid(InstancePtr != NULL);
    Xil_AssertNonvoid(MutexNumber < InstancePtr->Config.NumMutex);

    if (!(InstancePtr->Config.UserReg))
    {
        return XST_NO_FEATURE;
    }

    my_Mutex_WriteReg(InstancePtr->Config.BaseAddress, MutexNumber,
        XMU_USER_REG_OFFSET, User);

    return XST_SUCCESS;
}
#endif
