/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>

#include <asm/mips-boards/ohio.h>
#include <asm/mach_avm.h>
#include <asm/mach-ohio/hw_reset.h>

static struct _ohio_gpio_reset_bits {
    unsigned int Bit_Mask;
    unsigned int Polarity_Mask;
} ohio_gpio_reset_bits;

static spinlock_t ohio_reset_spinlock;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_reset_init(void) {
    spin_lock_init(&ohio_reset_spinlock);
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_put_device_into_reset(unsigned int Bit) {
    struct _hw_reset *RESET = (struct _hw_reset *)OHIO_RESET_BASE;
    if(Bit < OHIO_RESET_START_GPIO) {
        unsigned int flags;
        spin_lock_irqsave(&ohio_reset_spinlock, flags);
        RESET->non_reset.Reg &= ~(1 << Bit);
        spin_unlock_irqrestore(&ohio_reset_spinlock, flags);
        return 0;
    }
    if(Bit < OHIO_RESET_END_GPIO) {
        Bit -= OHIO_RESET_START_GPIO;
        ohio_gpio_out_bit(Bit, (ohio_gpio_reset_bits.Polarity_Mask & (1 << Bit)) ? 1 : 0);
        return 0;
    }
#if defined(CONFIG_VLYNQ_SUPPORT)
    if(Bit < OHIO_RESET_END_VIRTUAL) {
        return 0;
    }
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/
    return 1;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_take_device_out_of_reset(unsigned int Bit) {
    struct _hw_reset *RESET = (struct _hw_reset *)OHIO_RESET_BASE;
    if(Bit < OHIO_RESET_START_GPIO) {
        unsigned int flags;
        spin_lock_irqsave(&ohio_reset_spinlock, flags);
        RESET->non_reset.Reg |= (1 << Bit);
        spin_unlock_irqrestore(&ohio_reset_spinlock, flags);
        return 0;
    }
    if(Bit < OHIO_RESET_END_GPIO) {
        Bit -= OHIO_RESET_START_GPIO;
        ohio_gpio_out_bit(Bit, (ohio_gpio_reset_bits.Polarity_Mask & (1 << Bit)) ? 0 : 1);
        return 0;
    }
#if defined(CONFIG_VLYNQ_SUPPORT)
    if(Bit < OHIO_RESET_END_VIRTUAL) {
        return 0;
    }
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/
    return 1;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_reset_device(unsigned int Bit, unsigned int msec_delay) {
    unsigned int ret;

    ret = ohio_put_device_into_reset(Bit);
    if(ret) return ret;
    if(msec_delay)
        mdelay(msec_delay);
    ret = ohio_take_device_out_of_reset(Bit);
    if(ret) return ret;
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_register_reset_gpio(unsigned int gpio_pin, t_reset_gpio_polarity Polarity) {

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

    if(ohio_gpio_reset_bits.Bit_Mask & (1 << gpio_pin)) 
        return (unsigned int)-1;

    ohio_gpio_reset_bits.Bit_Mask      |= (1 << gpio_pin);
    if(Polarity)
        ohio_gpio_reset_bits.Polarity_Mask |=  (1 << gpio_pin);
    else
        ohio_gpio_reset_bits.Polarity_Mask &= ~(1 << gpio_pin);
        
    ohio_gpio_ctrl(gpio_pin, GPIO_PIN, GPIO_OUTPUT_PIN);

    return gpio_pin + OHIO_RESET_START_GPIO;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int ohio_release_reset_gpio(unsigned int Bit) {
    unsigned int gpio_pin = Bit - OHIO_RESET_START_GPIO;
    if(gpio_pin >= 32)
        return (unsigned int)-1;
    if(!(ohio_gpio_reset_bits.Bit_Mask & (1 << gpio_pin)))
        return (unsigned int)-1;
    ohio_gpio_reset_bits.Bit_Mask &= ~(1 << gpio_pin);
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int ohio_reset_status(void) {
    struct _hw_reset *RESET = (struct _hw_reset *)OHIO_RESET_BASE;
    
    return (unsigned int)RESET->reset_status.Bits.cause;
}

EXPORT_SYMBOL(ohio_put_device_into_reset);
EXPORT_SYMBOL(ohio_take_device_out_of_reset);
EXPORT_SYMBOL(ohio_reset_device);
EXPORT_SYMBOL(ohio_register_reset_gpio);
EXPORT_SYMBOL(ohio_release_reset_gpio);
EXPORT_SYMBOL(ohio_reset_status);

