/******************************************************************************
版权所有：	
文件名称：	gpio.c
文件版本：	01.01
创建作者:	zhaoyang
创建日期：	2023-12-14
功能说明：	gpio驱动程序。
其它说明：
修改记录：

[old]-->drop
gpio0: a0111000.gpio -> gpiochip480 -> GPIO0_0
gpio1: a0112000.gpio -> gpiochip448 -> GPIO1_0
gpio2: a0387000.gpio -> gpiochip416 -> GPIO2_0
gpio3: a0388000.gpio -> gpiochip384 -> GPIO3_0
gpio4: a0919000.gpio -> gpiochip352 -> GPIO4_0
gpio5: a091a000.gpio -> gpiochip320 -> GPIO5_0
gpio6: a091b000.gpio -> gpiochip288 -> GPIO6_0
gpio7: a091c000.gpio -> gpiochip256 -> GPIO7_0

PDO0_1		--> GPIO5_22	-->  gpio342
PDO1_1		--> GPIO5_23	-->  gpio343
PDO2_1		--> GPIO5_24	-->  gpio344
PDO3_1		--> GPIO5_25	-->  gpio345
KOUT0		--> GPIO4_29	-->  gpio381
KOUT1		--> GPIO4_30	-->  gpio382
KOUT2		--> GPIO4_31	-->  gpio383
KOUT3		--> GPIO5_0		-->  gpio320
KOUT4		--> GPIO5_1		-->  gpio321
KOUT5		--> GPIO5_2		-->  gpio322
KOUT6		--> GPIO5_3		-->  gpio323
KOUT7		--> GPIO5_4		-->  gpio324
KOUT8		--> GPIO5_5		-->  gpio325
KOUT9		--> GPIO5_6		-->  gpio326
// sunxi 20220530 KOUT10		--> GPIO5_7		-->  gpio327
// sunxi 20220530 KOUT_EN0	--> GPIO5_8		-->  gpio328
// sunxi 20220530 KOUT_EN1	--> GPIO5_9		-->  gpio329
KOUT10		--> GPIO2_21	-->  gpio437
KOUT_EN0	--> GPIO3_18	-->  gpio402
KOUT_EN1	--> GPIO3_19	-->  gpio403
RUN led		--> GPIO3_17	-->  gpio401
ERR led		--> GPIO3_20	-->  gpio404
// sunxi 20220609 esam_pwr    --> GPIO0_10	-->	 gpio490
esam_pwr    --> GPIO2_21	-->	 gpio437
WTD			--> GPIO0_31	-->  gpio511

[new]-->applay
gpio0: a0111000.gpio -> gpiochip0 -> GPIO0_0
gpio1: a0112000.gpio -> gpiochip32 -> GPIO1_0
gpio2: a0387000.gpio -> gpiochip64 -> GPIO2_0
gpio3: a0388000.gpio -> gpiochip96 -> GPIO3_0
gpio4: a0919000.gpio -> gpiochip128 -> GPIO4_0
gpio5: a091a000.gpio -> gpiochip160 -> GPIO5_0
gpio6: a091b000.gpio -> gpiochip192 -> GPIO6_0
gpio7: a091c000.gpio -> gpiochip224 -> GPIO7_0

PDO0_1		--> GPIO5_22	-->  gpio182
PDO1_1		--> GPIO5_23	-->  gpio183
PDO2_1		--> GPIO5_24	-->  gpio184
PDO3_1		--> GPIO5_25	-->  gpio185
KOUT0		--> GPIO4_29	-->  gpio157
KOUT1		--> GPIO4_30	-->  gpio158
KOUT2		--> GPIO4_31	-->  gpio159
KOUT3		--> GPIO5_0		-->  gpio160
KOUT4		--> GPIO5_1		-->  gpio161
KOUT5		--> GPIO5_2		-->  gpio162
KOUT6		--> GPIO5_3		-->  gpio163
KOUT7		--> GPIO5_4		-->  gpio164
KOUT8		--> GPIO5_5		-->  gpio165
KOUT9		--> GPIO5_6		-->  gpio166
// zhaoyang 20231214 KOUT10		--> GPIO5_7		-->  gpio167
// zhaoyang 20231214 KOUT_EN0	--> GPIO5_8		-->  gpio168
// zhaoyang 20231214 KOUT_EN1	--> GPIO5_9		-->  gpio169
KOUT10		--> GPIO2_21	-->  gpio85
KOUT_EN0	--> GPIO3_18	-->  gpio114
KOUT_EN1	--> GPIO3_19	-->  gpio115
RUN led		--> GPIO3_17	-->  gpio113
ERR led		--> GPIO3_20	-->  gpio116
// zhaoyang 20231214 esam_pwr    --> GPIO0_10	-->	 gpio10
esam_pwr    --> GPIO2_21	-->	 gpio85
WTD			--> GPIO0_31	-->  gpio31

*/

/*------------------------------- 头文件 --------------------------------------
*/
#include "bspconfig.h"
#include "gpio.h"
#include "head.h"


/*------------------------------- 宏定义 --------------------------------------
*/
#if CFG_BSP_DEBUG

#define _GPIO_DEBUG

#endif


/*------------------------------ 全局变量 -------------------------------------
*/
unsigned short kc_pwm0_stu;
unsigned short kc_pwm1_stu;

unsigned short kc_kout0_stu;
unsigned short kc_kout1_stu;

extern uint32_t g_yx_buf[2]; //预留64个遥信

/*------------------------------ 函数声明 -------------------------------------
*/
extern unsigned int change_di_ch(unsigned int di);

int gpio_val_fd[DO_NUM];

const int gpio_val[DO_NUM] = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105};

int gpio_val_fd_other[GPIO_NUM_OTHER];
const int gpio_val_other[GPIO_NUM_OTHER] ={39,164,359,197};

// static int gpio_watchdog_val = 0;
static int gpio_run_led_val = 0;

/*------------------------------ 外部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	gpio_init
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-06-26
函数说明：	gpio初始化。
参数说明：	无
返回值：  	成功返回0.
修改记录：
*/
int gpio_init(void)
{
    int i, j;
    int exportfd, directionfd;
    char num[10];
    char path[50] = "";

    //do
    exportfd = open("/sys/class/gpio/export", O_WRONLY);
    if (exportfd < 0)
    {
        printf("Cannot open GPIO to export it\n");
        return -1;
    }

    for (i = 0; i < DO_NUM; i ++)
    {
        sprintf(num, "%d", gpio_val[i]);
        write(exportfd, num, 4);
        gpio_val_fd[i] = -1;
    }

    for (i = 0; i < GPIO_NUM_OTHER; i ++)
    {
        sprintf(num, "%d", gpio_val_other[i]);
        write(exportfd, num, 4);
        gpio_val_fd_other[i] = -1;
    }

    close(exportfd);
#if 0	// TODO：需根据实际硬件重新对应匹配
    for (i = 0; i < DO_NUM; i ++)
    {
        sprintf(path, "/sys/class/gpio/gpio%d/value", gpio_val[i]);

        gpio_val_fd[i] = open(path, O_RDWR);
        if (gpio_val_fd[i] < 0)
        {
            printf("Cannot open GPIO%d value!\n", gpio_val[i]);
            for(j = 0; j < i; j ++)
            {
            	close(gpio_val_fd[j]);
            	gpio_val_fd[j] = -1;
            }
            return -4;
        }
    }

	// Update the direction of the GPIO to be an output
	for (i = 0; i < DO_NUM; i ++)
	{
		sprintf(path, "/sys/class/gpio/gpio%d/direction", gpio_val[i]);

		directionfd = open(path, O_RDWR);
		if (directionfd < 0)
		{
			printf("Cannot open GPIO%d direction!\n", gpio_val[i]);
			return -2;
		}
		write(directionfd, "out", 4);
		write(gpio_val_fd[i], "1", 2);
		close(directionfd);
	}
#endif
	msleep(10);// 延时10ms
	

    for (i = 0; i < GPIO_NUM_OTHER; i  ++)
    {
        sprintf(path, "/sys/class/gpio/gpio%d/value", gpio_val_other[i]);

        gpio_val_fd_other[i] = open(path, O_RDWR);
        if (gpio_val_fd_other[i] < 0)
        {
            printf("Cannot open GPIO%d value!\n", gpio_val_other[i]);
            for(j = 0; j < i; j ++)
            {
            	close(gpio_val_fd_other[j]);
            	gpio_val_fd_other[j] = -1;
            }
            // for(j = 0; j < DO_NUM; j ++)
            // {
            // 	close(gpio_val_fd[j]);
            // 	gpio_val_fd[j] = -1;
            // }
            return -5;
        }
    }

	for (i = 0; i < GPIO_NUM_OTHER; i ++)
	{
		sprintf(path, "/sys/class/gpio/gpio%d/direction", gpio_val_other[i]);

		directionfd = open(path, O_RDWR);
		if (directionfd < 0)
		{
			printf("Cannot open GPIO%d direction!\n", gpio_val_other[i]);
			return -3;
		}
		write(directionfd, "out", 4);
		close(directionfd);
	}

	kc_pwm0_stu = 0;
	kc_pwm1_stu = 0;
    kc_kout0_stu=0;
    kc_kout1_stu=0;

    //其他与底层GPIO相关的配置再此处添加 比如 LED WATCHDOG                  jack.liu 202009010
    // write(gpio_val_fd_other[WATCHDOG], "0", 2);//TODO: 需根据实际硬件重新对应匹配
    // gpio_watchdog_val = 0;
    write(gpio_val_fd_other[RUN_LED], "0", 2);
    gpio_run_led_val = 0;

	//上电默认切换到外部232串口
	//后续根据投退判断是否需要切换到线损的维护口
	write(gpio_val_fd_other[LINE_LOSS_SET], "0", 2);
	
    //首次上电，开出合闸，会导致第六路也开出，需要初始化关闭
//    write(gpio_val_fd[DO_OUT1], "1", 2);
#if 0	// TODO：需根据实际硬件重新对应匹配
    for (i = 0; i < DO_NUM; i ++)
    {
		write(gpio_val_fd[i], "1", 2);
    }
#endif
    return 0;
}

int gpio_exit(void)
{
	int i;
	int unexportfd;
    char num[10];

	unexportfd = open("/sys/class/gpio/unexport", O_WRONLY);
    if (unexportfd < 0)
    {
        printf("Cannot open GPIO to unexport it\n");
        return -1;
    }

    for (i = 0; i < DO_NUM; i ++)
    {
    	if(gpio_val_fd[i] >= 0)
    	{
    		close(gpio_val_fd[i]);
    		gpio_val_fd[i] = -1;
    	}
        sprintf(num, "%d", gpio_val[i]);
        write(unexportfd, num, 4);
    }

    for (i = 0; i < GPIO_NUM_OTHER; i ++)
    {
    	if(gpio_val_fd_other[i] >= 0)
    	{
    		close(gpio_val_fd_other[i]);
    		gpio_val_fd_other[i] = -1;
    	}
        sprintf(num, "%d", gpio_val_other[i]);
        write(unexportfd, num, 4);
    }

    close(unexportfd);

    return 0;
}

/******************************************************************************
函数名称：	gpio_get_kin
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-09-16
函数说明：	得到开入的状态。
参数说明：	无
返回值：  	返回开入的状态，每个bit代表一个开入量。
修改记录：
*/
unsigned int gpio_get_kin(unsigned int addr)
{
	register unsigned int dw;
	unsigned int value[3];

	value[0]  = *(volatile unsigned int *)addr;
	value[1]  = *(volatile unsigned int *)addr;
	value[2]  = *(volatile unsigned int *)addr;

	dw = (value[0] & value[1]) | (value[0] & value[2]) | (value[1] & value[2]);

	return dw;
}


/******************************************************************************
函数名称：	gpio_get_di
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	得到开入的状态
参数说明：	无
返回值：  	返回开入的状态，每个bit代表一个开入量。
修改记录：
*/

unsigned int gpio_get_di(void)
{
    unsigned int dw = 0;

	dw = g_yx_buf[0];//这里保存的是共享内存的yx值，156us更新一次

	return  change_di_ch(~dw);//取反---因为没有短接时，值为1. 所以要取反为0才行
}


/******************************************************************************
函数名称：	gpio_di_fj
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	读取开出返校遥信值,控制板遥信为YX17~YX18
			YX17:选择, YX18:执行

参数说明：	 无
返回值：  返回遥信值
修改记录：
*/
#ifdef DO_KOUT_CHECK
unsigned short gpio_di_fj(unsigned char index)//(void)
#else
unsigned short gpio_di_fj(void)
#endif
{
	unsigned int di=0;
	unsigned short ret=0;

	di = gpio_get_di();
#ifdef DO_KOUT_CHECK
    //V4-20;
	//ret = (unsigned short)((di >>19) & 0x0001);
	ret = (unsigned short)((di >>index) & 0x0001);
#else
	ret = (unsigned short)((di >> 23) & 0x0001);//dtu
#endif
	return ret;
}

static void gpio_kouten_do(int on, unsigned int kouten)
{
    // if (kouten > KOUT_EN1)
    //     return;

    // if(on)
    // {
    //     write(gpio_val_fd_other[KOUT_EN0+kouten], "1", 2);
    // }
    // else
    // {
    //     write(gpio_val_fd_other[KOUT_EN0+kouten], "0", 2);
    // }

}


/******************************************************************************
函数名称：	gpio_kout_do
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	开出

参数说明：	on: 1开出, 0回收
			kout: 那一路
返回值：  无
修改记录：
*/
void gpio_kout_do(int on, unsigned int kout)
{

    //if (kout > (DO_NUM-DO_OUT0))
    //    return;

	if(on)
	{

		if((kout==6) || (kout==7))
		{
			// gpio_kouten_do(1,KOUT_EN1);
			kc_kout1_stu |= (1 << kout);
		}
		else
		{
			// gpio_kouten_do(1,KOUT_EN0);
			kc_kout0_stu |= (1 << kout);
		}
		
		write(gpio_val_fd[kout+DO_OUT0], "0", 2);

	}
	else
	{

		write(gpio_val_fd[kout+DO_OUT0], "1", 2);

		if((kout==6) || (kout==7))
		{
			kc_kout1_stu &= ~(1 << kout);
			
		}
		else
		{
			kc_kout0_stu &= ~(1 << kout);
		}

		if ((kc_pwm1_stu== 0) &&(kc_kout1_stu == 0))
		{
			// gpio_kouten_do(0,KOUT_EN1);
		}
		
		if ((kc_kout0_stu == 0) && (kc_pwm0_stu==0))
		{
			// gpio_kouten_do(0,KOUT_EN0);
		}	

	}


}

/******************************************************************************
函数名称：	gpio_pwm_do
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	pwm开出，由于存在kc_pwm_stu，只允许在同一控制路径中开出(例如同一中断或同一线程).

参数说明：		on: 1开出, 0回收
返回值：  无
修改记录：
*/
void gpio_pwm_do(int on, unsigned int kout)
{
   // if (kout >= DO_NUM)
   //     return;

	if(on)
	{
		
		if((kout==0) || (kout==1))
		{
			// gpio_kouten_do(1,KOUT_EN1);
			kc_pwm1_stu |= (1 << kout);
		}
		else
		{
			// gpio_kouten_do(1,KOUT_EN0);
			kc_pwm0_stu |= (1 << kout);
		}

		write(gpio_val_fd[kout], "0", 2);

	}
	else
	{
		write(gpio_val_fd[kout], "1", 2);

		if((kout==0) || (kout==1))
		{
			kc_pwm1_stu &= ~(1 << kout);
			
		}
		else
		{
			kc_pwm0_stu &= ~(1 << kout);
		}

		if ((kc_pwm1_stu== 0) &&(kc_kout1_stu == 0))
		{
			// gpio_kouten_do(0,KOUT_EN1);

		}
		
		if ((kc_kout0_stu == 0) && (kc_pwm0_stu==0))
		{
			// gpio_kouten_do(0,KOUT_EN0);
		}	

	}

}

/******************************************************************************
函数名称：	gpio_get_wirelessin
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-09-16
函数说明：	得到无线遥控的状态。
参数说明：	无
返回值：  	返回开入的状态，每个bit代表一个开入量。
修改记录：
*/
unsigned int gpio_get_wirelessin(void)
{
	register unsigned int dw = 0;

	return dw;
}

unsigned int gpio_get_version(void)
{
	register unsigned int dw = 0;

	return dw;
}


unsigned int gpio_get_addr(void)
{
	register unsigned int dw = 0;

	return dw;
}



/******************************************************************************
函数名称：	gpio_watchdog_reset
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-09-16
函数说明：	复位看门狗。
参数说明：	无。
返回值：  	无。
修改记录：
*/
// void gpio_watchdog_reset(void)
// {

// 	if(gpio_watchdog_val)
// 	{
// 		gpio_watchdog_val = 0;
// 		write(gpio_val_fd_other[WATCHDOG], "0", 2);
// 	}
// 	else
// 	{
// 		gpio_watchdog_val = 1;
// 		write(gpio_val_fd_other[WATCHDOG], "1", 2);
// 	}

// 	return;
// }

void _led_run_err(void)
{
	//运行指示灯,每隔0.5s改变状态一次
	if(!gpio_run_led_val)
	{
		gpio_run_led_val = 1;
		write(gpio_val_fd_other[RUN_LED], "0", 2);

		//错误指示灯,跟随RUN灯闪动
		if(rt_err_count())
		{
			write(gpio_val_fd_other[ERR_LED], "0", 2);
		}

	}
	else
	{
		gpio_run_led_val = 0;
		write(gpio_val_fd_other[RUN_LED], "1", 2);
		write(gpio_val_fd_other[ERR_LED], "1", 2);
	}
}

void esam_power_ctrl(int on)
{
	if(on)
		write(gpio_val_fd_other[ESAM_PWR], "1", 2);
	else
		write(gpio_val_fd_other[ESAM_PWR], "0", 2);
}

/*------------------------------ 内部函数 -------------------------------------
*/


/*------------------------------ 测试函数 -------------------------------------
*/
#ifdef _GPIO_DEBUG
#include "ustimer.h"
#include "rt.h"

int gpio_test(void)
{
	int ret = 0;
	//unsigned int i;

	//gpio_kouten_do(1,KOUT_EN1);

#if 1
	while(1)
	{


			sleep(5);

			gpio_kout_do(1,DO_OUT2-DO_OUT0);
			//gpio_kout_do(1,DO_OUT3-DO_OUT0);
			// gpio_pwm_do(1,DO_PWM1);



			sleep(5);

			gpio_kout_do(0,DO_OUT2-DO_OUT0);
			//gpio_kout_do(0,DO_OUT3-DO_OUT0);
			// gpio_pwm_do(0,DO_PWM1);

/*
			
		for(i=DO_OUT0; i<DO_NUM; i++)
		{
			rt_printf("kout number = %d!\n\r", i);
			//msleep();
			sleep(1);
			gpio_kout_do(1,i-DO_OUT0);
			sleep(1);
			gpio_kout_do(0,i-DO_OUT0);


		}
*/
		
	}
#endif
	return ret;
}
#endif
