#include <ucps2-intrin.h>
#include "spinlock.h"


#define OSP_APE_SPAIN_LOCK_ADDR   (0x931FF00-0x400)

#define SPINLOCK_BIT_MAP_ADDR     (0x931fb40)

#define DEBUG_NR   16

unsigned int spin_debug_switch = 0;
unsigned int  g_unspinlock_loop = 0;
unsigned int g_ddr_spinlock_loop = 0;

typedef unsigned int uint32_t;

#define spin_do_write(a,v)	__ucps2_store_ext_mem((void *)(a), (uint32_t)v, f_W)
#define spin_do_read(ptr)    __ucps2_load_ext_mem_v((char*)(ptr), f_W)



unsigned int  g_spinlock_count[DEBUG_NR]  = {0};
unsigned int  g_unspinlock_count[DEBUG_NR] = {0};
unsigned int  g_spinlock_loop[DEBUG_NR] = {0};
unsigned int  g_spin_count[DEBUG_NR] = {0};

unsigned char *use_flag = (char*)SPINLOCK_BIT_MAP_ADDR;
spin_debug_t *spin_debug_info;


static int test_bit(void *s,unsigned int index)
{
    unsigned char *f_bit = s;
    f_bit += index >>3;
    return ((*f_bit&(1 <<((index%8)))) != 0);
}

static void set_bit(void *s,unsigned int index)
{
    unsigned char *f_bit = s;
    f_bit += index >> 3;
    *f_bit |= 1<<(index%8);
}

static void clear_bit(void *s,unsigned int index)
{
    unsigned char *f_bit = s;
    f_bit += index >> 3;
    *f_bit &= ~(1 <<(index%8));
}

/*
    函数:smart_spin_debug_init
    参数:lock_idx
    参数范围：1--15
*/

int smart_spin_debug_init(int lock_idx,unsigned int debug_base_addr, int len_bytes)
{
    if((lock_idx < 1)||(lock_idx > 15))
    {
        return -1;
    }
    if(debug_base_addr <= 0x10000000)
    {
        return -1;
    }
    if (len_bytes < 16)
    {
        return -2;
    }
    spin_debug_switch = 1;
    spin_debug_info = (spin_debug_t *)debug_base_addr;

    return 0;
}

void spin_debug(void * addr , uint32_t v)
{
    if(spin_debug_switch == 1)
    {
        spin_do_write(addr, v);
        __ucps2_synch(0);
    }
}



int smart_spinlock_init(int lock_index)
{
    int  i;
    if((lock_index < 1)||(lock_index > 15))
    {
        return -1;
    }
    if(test_bit(use_flag,lock_index))
    {
        return -2; //
    }
    set_bit(use_flag,lock_index);
    *(volatile unsigned int *)(OSP_APE_SPAIN_LOCK_ADDR+lock_index*4) = 0;

    return 0;

}
void smart_spinlock(int lock_index)
{
    spinlock_t lockval;
    spinlock_t *lock;
    int ret;
    int owner;
    int next;
    if (lock_index > SLOCK_MAX)
    {
        return;
    }
    else
    {
        lock = (spinlock_t*)(OSP_APE_SPAIN_LOCK_ADDR+lock_index*4);
    }

    while (1)
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        next = ((lockval.slock >> 16) & 0x0000ffff);
        owner = next;
        next++;
        lockval.slock &=0x0000ffff;
        lockval.slock |= ((next & 0xffff) << 16);
        ret = __ucps2_atomicst((int *)lock, lockval.slock, f_W);
        if (1 == ret)
        {
            break;
        }
        else
        {
            g_spin_count[lock_index]++;
            spin_debug(&(spin_debug_info->spin_per_ape.g_spin_count) , g_spin_count[lock_index]);
        }
    }
    do
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        g_spinlock_loop[lock_index]++;
        spin_debug(&(spin_debug_info->spin_per_ape.spinlock_loop) , g_spinlock_loop[lock_index]);
    } while (owner != ((lockval.slock & 0x0000ffff)));
     g_spinlock_count[lock_index]++;
     spin_debug(&(spin_debug_info->spin_per_ape.spinlock_count) , g_spinlock_count[lock_index]);

}


void smart_spinunlock(int lock_index)
{
    spinlock_t lockval;
    int ret;
    int atomfail_unlock = 0;
    spinlock_t * lock;
    unsigned int temp_owner = 0;
    if (lock_index > SLOCK_MAX)
    {
        return;
    }
    else
    {
        lock = (spinlock_t*)(OSP_APE_SPAIN_LOCK_ADDR+lock_index*4);
    }
    while (1)
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        temp_owner = (lockval.slock & 0x0000ffff);
        temp_owner ++;
        lockval.slock &= 0xffff0000;
        lockval.slock |= (temp_owner & 0x0000ffff);
        ret = __ucps2_atomicst((int *)lock, lockval.slock, f_W);
        if (1 == ret)
        {
            break;
        }
        else
        {
        //  g_unspinlock_loop++;
        //  spin_debug(&(spin_debug_info->spin_per_ape.unspinlock_loop) , g_unspinlock_loop);
        }
    }
    g_unspinlock_count[lock_index]++;
    spin_debug(&(spin_debug_info->spin_per_ape.unspinlock_count) , g_unspinlock_count[lock_index]);

}


/*
    参数:lock_idx
    参数范围：1--15
*/
int smart_get_spin_loop_cnt(int lock_idx)
{
    return g_spinlock_loop[lock_idx];
}

/*
    参数:lock_idx
    参数范围：1--15
*/

int smart_get_spin_cnt(int lock_idx)
{
    return g_spin_count[lock_idx];
}

/*
    参数:lock_idx
    参数范围：1--15
*/

int smart_get_spinlock_cnt(int lock_idx)
{
    return g_spinlock_count[lock_idx];
}

/*
    参数:lock_idx
    参数范围：1--15
*/

int smart_get_unspinlock_cnt(int lock_idx)
{
    return g_unspinlock_count[lock_idx];
}


int smart_ddr_spinlock_init(ddr_spinlock_t *val)
{
    int  i;
    int init_val = 0;

    if(spin_do_read(val->flag_addr) == 0xab3f)
    {
        return -2;
    }
    spin_do_write(val->flag_addr,0xab3f);
    spin_do_write(val->lock_addr,0);
    
    return 0;
}



void smart_ddr_spinlock(ddr_spinlock_t *val)
{
    spinlock_t lockval;
    volatile unsigned int *lock;
    int ret;
    int owner;
    int next;
    int loop;

    lock = (volatile unsigned int*)(val->lock_addr);
    while (1)
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        next = ((lockval.slock >> 16) & 0x0000ffff);
        owner = next;
        next++;
        lockval.slock &=0x0000ffff;
        lockval.slock |= ((next & 0xffff) << 16);
        ret = __ucps2_atomicst((int *)lock, lockval.slock, f_W);
        if (1 == ret)
        {
            break;
        }
        else
        {
        
        }
    }
    do
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        g_ddr_spinlock_loop++;
    } while (owner != ((lockval.slock & 0x0000ffff)));

}


void smart_ddr_spinunlock(ddr_spinlock_t *val)
{
    spinlock_t lockval;
    int ret;
    volatile unsigned int * lock;
    unsigned int temp_owner = 0;
    lock =(volatile unsigned int*)val->lock_addr;

    while (1)
    {
        lockval.slock = __ucps2_atomicld((int *)lock, f_W);
        temp_owner = (lockval.slock & 0x0000ffff);
        temp_owner ++;
        lockval.slock &= 0xffff0000;
        lockval.slock |= (temp_owner & 0x0000ffff);
        ret = __ucps2_atomicst((int *)lock, lockval.slock, f_W);
        if (1 == ret)
        {
            break;
        }
        else
        {
//          g_ddr_unspinlock_loop++;
//          spin_do_write(val->unlock_loop_addr,g_ddr_unspinlock_loop);

        }
    }


}
