/*******************************************************************************
 * (c) Copyright 2008 Actel Corporation.  All rights reserved.
 * 
 * SmartFusion microcontroller subsystem GPIO bare metal driver implementation.
 *
 * SVN $Revision: 1753 $
 * SVN $Date: 2009-12-11 15:12:18 +0000 (Fri, 11 Dec 2009) $
 */
#include "mss_gpio.h"
#include "../../CMSIS/mss_assert.h"

#ifdef __cplusplus
extern "C" {
#endif 

/*-------------------------------------------------------------------------*//**
*
*/
#define GPIO_INT_ENABLE_MASK        (uint32_t)0x00000008UL
#define OUTPUT_BUFFER_ENABLE_MASK   0x00000004UL

#define NB_OF_GPIO  (uint32_t)32

/*-------------------------------------------------------------------------*//**
 * Lookup table of GPIO configuration registers address indexed on GPIO ID.
 */
static uint32_t volatile * const g_config_reg_lut[NB_OF_GPIO] =
{
    &(GPIO->GPIO_0_CFG),
    &(GPIO->GPIO_1_CFG),
    &(GPIO->GPIO_2_CFG),
    &(GPIO->GPIO_3_CFG),
    &(GPIO->GPIO_4_CFG),
    &(GPIO->GPIO_5_CFG),
    &(GPIO->GPIO_6_CFG),
    &(GPIO->GPIO_7_CFG),
    &(GPIO->GPIO_8_CFG),
    &(GPIO->GPIO_9_CFG),
    &(GPIO->GPIO_10_CFG),
    &(GPIO->GPIO_11_CFG),
    &(GPIO->GPIO_12_CFG),
    &(GPIO->GPIO_13_CFG),
    &(GPIO->GPIO_14_CFG),
    &(GPIO->GPIO_15_CFG),
    &(GPIO->GPIO_16_CFG),
    &(GPIO->GPIO_17_CFG),
    &(GPIO->GPIO_18_CFG),
    &(GPIO->GPIO_19_CFG),
    &(GPIO->GPIO_20_CFG),
    &(GPIO->GPIO_21_CFG),
    &(GPIO->GPIO_22_CFG),
    &(GPIO->GPIO_23_CFG),
    &(GPIO->GPIO_24_CFG),
    &(GPIO->GPIO_25_CFG),
    &(GPIO->GPIO_26_CFG),
    &(GPIO->GPIO_27_CFG),
    &(GPIO->GPIO_28_CFG),
    &(GPIO->GPIO_29_CFG),
    &(GPIO->GPIO_30_CFG),
    &(GPIO->GPIO_31_CFG)
};

/*-------------------------------------------------------------------------*//**
 * Lookup table of Cortex-M3 GPIO interrupt number indexed on GPIO ID.
 */
static const IRQn_Type g_gpio_irqn_lut[NB_OF_GPIO] =
{
    GPIO0_IRQn,
    GPIO1_IRQn,
    GPIO2_IRQn,
    GPIO3_IRQn,
    GPIO4_IRQn,
    GPIO5_IRQn,
    GPIO6_IRQn,
    GPIO7_IRQn,
    GPIO8_IRQn,
    GPIO9_IRQn,
    GPIO10_IRQn,
    GPIO11_IRQn,
    GPIO12_IRQn,
    GPIO13_IRQn,
    GPIO14_IRQn,
    GPIO15_IRQn,
    GPIO16_IRQn,
    GPIO17_IRQn,
    GPIO18_IRQn,
    GPIO19_IRQn,
    GPIO20_IRQn,
    GPIO21_IRQn,
    GPIO22_IRQn,
    GPIO23_IRQn,
    GPIO24_IRQn,
    GPIO25_IRQn,
    GPIO26_IRQn,
    GPIO27_IRQn,
    GPIO28_IRQn,
    GPIO29_IRQn,
    GPIO30_IRQn,
    GPIO31_IRQn
};

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_init
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_init( void )
{
    uint32_t i;
    
    /* reset MSS GPIO hardware */
    SYSREG->SOFT_RST_CR |= SYSREG_GPIO_SOFTRESET_MASK;
    /* Clear any previously pended MSS GPIO interrupt */
    for ( i = 0U; i < NB_OF_GPIO; ++i )
    {
        NVIC_ClearPendingIRQ( g_gpio_irqn_lut[i] );
    }
    /* Take MSS GPIO hardware out of reset. */
    SYSREG->SOFT_RST_CR &= ~SYSREG_GPIO_SOFTRESET_MASK;
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_config
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_config
(
    mss_gpio_id_t port_id,
    uint32_t config
)
{
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );

    if ( gpio_idx < NB_OF_GPIO )
    {
        *(g_config_reg_lut[gpio_idx]) = config;
    }
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_set_output
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_set_output
(
    mss_gpio_id_t       port_id,
    uint8_t             value
)
{
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );
    
    if ( gpio_idx < NB_OF_GPIO )
    {
        GPIO_BITBAND->GPIO_OUT[gpio_idx] = (uint32_t)value;
    }
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_drive_inout
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_drive_inout
(
    mss_gpio_id_t port_id,
    mss_gpio_inout_state_t inout_state
)
{
    uint32_t outputs_state;
    uint32_t config;
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );
    
    if ( gpio_idx < NB_OF_GPIO )
    {
        switch( inout_state )
        {
        case MSS_GPIO_DRIVE_HIGH:
            /* Set output high */
            outputs_state = GPIO->GPIO_OUT;
            outputs_state |= (uint32_t)1 << gpio_idx;
            GPIO->GPIO_OUT = outputs_state;
            /* Enable output buffer */
            config = *(g_config_reg_lut[gpio_idx]);
            config |= OUTPUT_BUFFER_ENABLE_MASK;
            *(g_config_reg_lut[gpio_idx]) = config;
            break;
            
        case MSS_GPIO_DRIVE_LOW:
            /* Set output low */
            outputs_state = GPIO->GPIO_OUT;
            outputs_state &= ~((uint32_t)((uint32_t)1 << gpio_idx));
            GPIO->GPIO_OUT = outputs_state;
            /* Enable output buffer */
            config = *(g_config_reg_lut[gpio_idx]);
            config |= OUTPUT_BUFFER_ENABLE_MASK;
            *(g_config_reg_lut[gpio_idx]) = config;
            break;
            
        case MSS_GPIO_HIGH_Z:
            /* Disable output buffer */
            config = *(g_config_reg_lut[gpio_idx]);
            config &= ~OUTPUT_BUFFER_ENABLE_MASK;
            *(g_config_reg_lut[gpio_idx]) = config;
            break;
            
        default:
            ASSERT(0);
            break;
        }
    }
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_enable_irq
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_enable_irq
(
    mss_gpio_id_t port_id
)
{
    uint32_t cfg_value;
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );
    
    if ( gpio_idx < NB_OF_GPIO )
    {
        cfg_value = *(g_config_reg_lut[gpio_idx]);
        *(g_config_reg_lut[gpio_idx]) = (cfg_value | GPIO_INT_ENABLE_MASK);
        NVIC_EnableIRQ( g_gpio_irqn_lut[gpio_idx] );
    }
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_disable_irq
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_disable_irq
(
    mss_gpio_id_t port_id
)
{
    uint32_t cfg_value;
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );

    if ( gpio_idx < NB_OF_GPIO )
    {
        cfg_value = *(g_config_reg_lut[gpio_idx]);
        *(g_config_reg_lut[gpio_idx]) = (cfg_value & ~GPIO_INT_ENABLE_MASK);
    }
}

/*-------------------------------------------------------------------------*//**
 * MSS_GPIO_clear_irq
 * See "mss_gpio.h" for details of how to use this function.
 */
void MSS_GPIO_clear_irq
(
    mss_gpio_id_t port_id
)
{
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );
    
    if ( gpio_idx < NB_OF_GPIO )
    {
        GPIO->GPIO_IRQ = ((uint32_t)1) << gpio_idx;
        NVIC_ClearPendingIRQ( g_gpio_irqn_lut[gpio_idx] );
    }
}

#ifdef __cplusplus
}
#endif

