#include <asm/avalanche/avalanche_map.h>
#include <asm/avalanche/generic/avalanche_misc.h>
#include <asm/avalanche/sangam/hw_stack.h>
#include <linux/module.h>

static unsigned int avalanche_vbus_freq;
static unsigned char avalanche_gpio_link_count[32];

REMOTE_VLYNQ_DEV_RESET_CTRL_FN p_remote_vlynq_dev_reset_ctrl = NULL;
EXPORT_SYMBOL(p_remote_vlynq_dev_reset_ctrl);

/*****************************************************************************
 * Reset Control Module.
 *****************************************************************************/
int avalanche_reset_ctrl(AVALANCHE_RESET_MODULE_T _module_reset_bit, AVALANCHE_RESET_CTRL_T reset_ctrl)
{
    volatile unsigned int *reset_reg = (unsigned int*) AVALANCHE_RST_CTRL_PRCR;
    unsigned int module_reset_bit = (unsigned int)_module_reset_bit;

    if(module_reset_bit >= 32 && module_reset_bit < 64)
        return 0;

    if(module_reset_bit >= 64)
    {
        if(p_remote_vlynq_dev_reset_ctrl)
            return(p_remote_vlynq_dev_reset_ctrl(module_reset_bit - 64, reset_ctrl));
        else
            return 1;
    }
    
    if(reset_ctrl == OUT_OF_RESET)
        *reset_reg |= 1 << module_reset_bit;
    else
        *reset_reg &= ~(1 << module_reset_bit);
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
AVALANCHE_RESET_CTRL_T avalanche_get_reset_status(AVALANCHE_RESET_MODULE_T module_reset_bit)
{
    volatile unsigned int *reset_reg = (unsigned int*) AVALANCHE_RST_CTRL_PRCR;

    return (((*reset_reg) & (1 << (unsigned int)module_reset_bit)) ? OUT_OF_RESET : IN_RESET );
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avalanche_sys_reset(AVALANCHE_SYS_RST_MODE_T mode)
{
    volatile unsigned int *sw_reset_reg = (unsigned int*) AVALANCHE_RST_CTRL_SWRCR;
    *sw_reset_reg =  mode;
}

#define AVALANCHE_RST_CTRL_RSR_MASK 0x3

AVALANCHE_SYS_RESET_STATUS_T avalanche_get_sys_last_reset_status()
{
    volatile unsigned int *sys_reset_status = (unsigned int*) AVALANCHE_RST_CTRL_RSR;

    return ( (AVALANCHE_SYS_RESET_STATUS_T) (*sys_reset_status & AVALANCHE_RST_CTRL_RSR_MASK) );
}


/*****************************************************************************
 * Power Control Module
 *****************************************************************************/
#define AVALANCHE_GLOBAL_POWER_DOWN_MASK    0x3FFFFFFF      /* bit 31, 30 masked */
#define AVALANCHE_GLOBAL_POWER_DOWN_BIT     30              /* shift to bit 30, 31 */


void avalanche_power_ctrl(unsigned int module_power_bit, AVALANCHE_POWER_CTRL_T power_ctrl)
{
    volatile unsigned int *power_reg = (unsigned int*)AVALANCHE_POWER_CTRL_PDCR;

    if (power_ctrl == POWER_CTRL_POWER_DOWN)
        /* power down the module */
        *power_reg |= (1 << module_power_bit);
    else
        /* power on the module */
        *power_reg &= (~(1 << module_power_bit));
}

AVALANCHE_POWER_CTRL_T avalanche_get_power_status(unsigned int module_power_bit)
{
    volatile unsigned int *power_status_reg = (unsigned int*)AVALANCHE_POWER_CTRL_PDCR;

    return (((*power_status_reg) & (1 << module_power_bit)) ? POWER_CTRL_POWER_DOWN : POWER_CTRL_POWER_UP);
}

void avalanche_set_global_power_mode(AVALANCHE_SYS_POWER_MODE_T power_mode)
{
    volatile unsigned int *power_status_reg = (unsigned int*)AVALANCHE_POWER_CTRL_PDCR;

    *power_status_reg &= AVALANCHE_GLOBAL_POWER_DOWN_MASK;
    *power_status_reg |= ( power_mode << AVALANCHE_GLOBAL_POWER_DOWN_BIT);
}

AVALANCHE_SYS_POWER_MODE_T avalanche_get_global_power_mode(void)
{
    volatile unsigned int *power_status_reg = (unsigned int*)AVALANCHE_POWER_CTRL_PDCR;

    return((AVALANCHE_SYS_POWER_MODE_T) (((*power_status_reg) & (~AVALANCHE_GLOBAL_POWER_DOWN_MASK)) 
                                           >> AVALANCHE_GLOBAL_POWER_DOWN_BIT));
}

#if defined (CONFIG_AVALANCHE_GENERIC_GPIO)
/*****************************************************************************
 * GPIO  Control
 *****************************************************************************/

/****************************************************************************
 * FUNCTION: avalanche_gpio_init
 ***************************************************************************/
void avalanche_gpio_init(void)
{
    volatile unsigned int *reset_reg = (unsigned int*) AVALANCHE_RST_CTRL_PRCR;
    *reset_reg |= (1 << AVALANCHE_GPIO_RESET_BIT);
}

/****************************************************************************
 * FUNCTION: avalanche_gpio_ctrl
 ***************************************************************************/
int avalanche_gpio_ctrl(unsigned int gpio_pin,
                        AVALANCHE_GPIO_PIN_MODE_T pin_mode,
                        AVALANCHE_GPIO_PIN_DIRECTION_T pin_direction)
{
    volatile unsigned int *gpio_ctrl = (unsigned int*)AVALANCHE_GPIO_ENBL;

    if(gpio_pin >= 32)
        return(-1);

    if(pin_mode == GPIO_PIN)
    {
        *gpio_ctrl |= (1 << gpio_pin);

	gpio_ctrl = (unsigned int*)AVALANCHE_GPIO_DIR;
        
        if(pin_direction == GPIO_INPUT_PIN)
            *gpio_ctrl |=  (1 << gpio_pin);
        else
            *gpio_ctrl &= ~(1 << gpio_pin);
    }
    else /* FUNCTIONAL PIN */
    {
        *gpio_ctrl &= ~(1 << gpio_pin);
    }
  
    return (0);
}

/****************************************************************************
 * FUNCTION: avalanche_gpio_out
 ***************************************************************************/
int avalanche_gpio_out_bit(unsigned int gpio_pin, int value)
{
    volatile unsigned int *gpio_out = (unsigned int*) AVALANCHE_GPIO_DATA_OUT;
 
    if(gpio_pin >= 32)
        return(-1);
    
    if(value == TRUE)
        *gpio_out |= 1 << gpio_pin;
    else
	*gpio_out &= ~(1 << gpio_pin);

    return(0);
}

/****************************************************************************
 * FUNCTION: avalanche_gpio_in
 ***************************************************************************/
int avalanche_gpio_in_bit(unsigned int gpio_pin)
{
    volatile unsigned int *gpio_in = (unsigned int*) AVALANCHE_GPIO_DATA_IN;
 
    if(gpio_pin >= 32)
        return(-1);
    
    return ((*gpio_in) & (1 << gpio_pin));
}

/****************************************************************************
 * FUNCTION: avalanche_gpio_out_val
 ***************************************************************************/
int avalanche_gpio_out_value(unsigned int out_val, unsigned int out_mask, 
                           unsigned int reg_index)
{
    volatile unsigned int *gpio_out = (unsigned int*) AVALANCHE_GPIO_DATA_OUT;

    if(reg_index > 0)
        return(-1);

    *gpio_out &= ~out_mask;
    *gpio_out |= out_val;

    return(0);
}

/****************************************************************************
 * FUNCTION: avalanche_gpio_in_value
 ***************************************************************************/
int avalanche_gpio_in_value(unsigned int* in_val, unsigned int reg_index)
{
    volatile unsigned int *gpio_in = (unsigned int*) AVALANCHE_GPIO_DATA_IN;
 
    if(reg_index > 0)
        return(-1);

    *in_val = *gpio_in;

    return (0);
}

#endif

/***********************************************************************
 *
 *    Wakeup Control Module for TNETV1050 Communication Processor
 *
 ***********************************************************************/

#define AVALANCHE_WAKEUP_POLARITY_BIT   16

void avalanche_wakeup_ctrl(AVALANCHE_WAKEUP_INTERRUPT_T wakeup_int,
                           AVALANCHE_WAKEUP_CTRL_T      wakeup_ctrl,
                           AVALANCHE_WAKEUP_POLARITY_T  wakeup_polarity)
{
    volatile unsigned int *wakeup_status_reg = (unsigned int*) AVALANCHE_WAKEUP_CTRL_WKCR;

    /* enable/disable */
    if (wakeup_ctrl == WAKEUP_ENABLED)
        /* enable wakeup */
        *wakeup_status_reg |= wakeup_int;
    else
        /* disable wakeup */
        *wakeup_status_reg &= (~wakeup_int);

    /* set polarity */
    if (wakeup_polarity == WAKEUP_ACTIVE_LOW)
        *wakeup_status_reg |=  (wakeup_int << AVALANCHE_WAKEUP_POLARITY_BIT);
    else
        *wakeup_status_reg &= ~(wakeup_int << AVALANCHE_WAKEUP_POLARITY_BIT);
}

void avalanche_set_vbus_freq(unsigned int new_vbus_freq)
{
    avalanche_vbus_freq = new_vbus_freq;
}

unsigned int avalanche_get_vbus_freq()
{
    return(avalanche_vbus_freq);
}

unsigned int avalanche_get_chip_version_info()
{
    return(*(volatile unsigned int*)AVALANCHE_CVR);
}

#if 0
unsigned int avalanche_is_mdix_onchip(void)
{
#define SANGAM_2_0_VERSION  0x2005
    if((*(volatile unsigned int*)AVALANCHE_CVR) == SANGAM_2_0_VERSION)
        return 1;
    else
        return 0;

}
#endif

SET_MDIX_ON_CHIP_FN_T p_set_mdix_on_chip_fn = NULL;

int avalanche_set_mdix_on_chip(unsigned int base_addr, unsigned int operation)
{
    if(p_set_mdix_on_chip_fn)
        return (p_set_mdix_on_chip_fn(base_addr, operation));
    else
        return(-1);
}

unsigned int avalanche_is_mdix_on_chip(void)
{
    return(p_set_mdix_on_chip_fn ? 1:0);
}

/* software abstraction for HAL */

#include <linux/slab.h>

void* os_platform_malloc(unsigned int size)
{
 return kmalloc(size,GFP_KERNEL);
}
extern void  os_platform_free(void *p)
{
kfree(p);
}


void* os_platform_memset(void*p, int num, unsigned int size)
{
        return memset(p,num,size);
}


/*------------------------------------------------------------------------------------------*\
 *
 *      Stack Aufbau:
 *
 *      0x2000 + base                   : start-of-stack
 *      0x1000 + base                   : z.B. aktuelle stand
 *
 *      0x0000 + sizeof(*current) + base: stack-max
 *      0x0000 + base                   : current
 *
\*------------------------------------------------------------------------------------------*/
unsigned int kernel_stack_check(unsigned int needed) {
    unsigned int *stack_pointer = &needed;
    unsigned int _current     = (unsigned int)current & 0xFFFFFF;
    unsigned int _stack       = (unsigned int)stack_pointer & 0xFFFFFF;
    /*--- unsigned int _stack_start = _stack + 0x2000; ---*/
    unsigned int _stack_max   = _current + sizeof(*current);
    unsigned int _stack_free  = _stack - _stack_max;

    /*--- printk("[kernel_stack_check]  stack=0x%x current=0x%x max=0x%x free=0x%x\n", ---*/
            /*--- _stack, _current, _stack_max, _stack_free); ---*/

    if(_stack_free <= needed) {
        static char Buffer[80];
        strcpy(Buffer, "kernel stack overflow: ");
        strcat(Buffer, current->comm);
        panic(Buffer);
    }

    return _stack_free;
}


EXPORT_SYMBOL(kernel_stack_check);


EXPORT_SYMBOL(avalanche_reset_ctrl);
EXPORT_SYMBOL(avalanche_get_reset_status);
EXPORT_SYMBOL(avalanche_sys_reset);
EXPORT_SYMBOL(avalanche_get_sys_last_reset_status);
EXPORT_SYMBOL(avalanche_power_ctrl);
EXPORT_SYMBOL(avalanche_get_power_status);
EXPORT_SYMBOL(avalanche_set_global_power_mode);
EXPORT_SYMBOL(avalanche_get_global_power_mode);
EXPORT_SYMBOL(avalanche_set_mdix_on_chip);
EXPORT_SYMBOL(avalanche_is_mdix_on_chip);
EXPORT_SYMBOL(os_platform_malloc);
EXPORT_SYMBOL(os_platform_free);
EXPORT_SYMBOL(os_platform_memset);



#if defined (CONFIG_AVALANCHE_GENERIC_GPIO)
EXPORT_SYMBOL(avalanche_gpio_init);
EXPORT_SYMBOL(avalanche_gpio_ctrl);
EXPORT_SYMBOL(avalanche_gpio_out_bit);
EXPORT_SYMBOL(avalanche_gpio_in_bit);
EXPORT_SYMBOL(avalanche_gpio_out_value);
EXPORT_SYMBOL(avalanche_gpio_in_value);
#endif

EXPORT_SYMBOL(avalanche_set_vbus_freq);
EXPORT_SYMBOL(avalanche_get_vbus_freq);

EXPORT_SYMBOL(avalanche_get_chip_version_info);

