/*
 * shw_tool:
 *
 * Test the libswitchhw library by calling various of its components.
 *
 *  Created on: Oct 30, 2012
 *  Authors:
 * 		- Benoit RAT
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License...
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "switch_hw.h"

//#include "pio_pins.h"
#include "pio.h"

#include <sys/ioctl.h>
#include <at91_softpwm.h>
#include <fcntl.h>
#include <util.h>

#include <fan.h>
#include <fpga_io.h>
#include <sfp_lib.h>
#include <i2c_io.h>

#include "sysmon-regs.h"

#include <shw_io.h>

static char * pgr_name;
static char * cmd_name;

//extern const pio_pin_t* _all_cpu_gpio_pins[];
//extern pio_pin_t* _all_fpga_gpio_pins[];

//--------------------------------------- tools functions

void print_args(int argc, char *argv[])
{
	printf("argc=%d: ",argc);
	while(argc>0)
	{
		printf("%s, ",argv[0]);
		argc--;
		argv++;
	}
	printf("\n");
}


int atoideflim(char* arg,int defval,int min, int max)
{
	int ret=atoidef(arg,defval);
	if(min <= ret && ret <= max) return ret;
	else return defval;
}

void shw_pio_printf(const pio_pin_t *pio)
{
	printf("PIO_%c(%d) mode=%d dir=%s\t",
			(char)((int)'A'+pio->port-1), pio->pin, pio->mode, ((pio->dir>0)?"OUT":"IN"));
	if(pio->dir==0) printf(" => %d\n", shw_pio_get(pio));
	//else printf(" x=> %x\n", (_readl(_pio_base[3][pio->port])));
	else printf("\n");
}



/**
 * Change the fan speed of the PWM.
 * Note: It seems that it is not working properly at the moment.
 */
int fan_tool(int argc, char **argv)
{
	char opt=0; int optind=1;
	uint8_t f_help=0;
	int bmask=0xFF, speed=0, timeout=0;

	print_args(argc,argv);

	while ((opt = getopt(argc, argv, "m:t:h?")) != 0xFF)
	{
		optind++;
		switch (opt)
		{
		case 'm': bmask=atoideflim(optarg,bmask,0,0xFF); optind++; break;
		case 't': timeout=atoideflim(optarg,timeout,-1,0xFFFFFF); optind++; break;
		case 'h':
		case '?':
			f_help=1;
			break;
		default:
			printf("find: illegal option %c (%x)\n", opt,opt);
			break;
		}
	}
	argc-=optind;
	argv+=optind;

	print_args(argc,argv);
	speed=atoideflim(argv[0],speed,0,100);

	if(f_help)
	{
		printf("Usage %s %s [OPTIONS] <speed in %>\n",pgr_name,cmd_name);
		printf(""
				"OPTIONS:\n"
				"	-m bit mask to select the FAN: 0x07 (1, 2 & 3), 0x2 (only 2) (def=0xFF)\n"
				"	-t timeout value in s for updating pwm (def=0)\n"
				"	-h show this help\n"
				""
		);
		return;
	}

	TRACE(TRACE_INFO,"Starting Fan Test at %d%% (m=0x%x,t=%ds)", speed,bmask,timeout);

	shw_pio_mmap_init();
	shw_fpga_mmap_init();
	shw_io_init();
	shw_io_configure_all();
	shw_init_fans();

	if(timeout>=0) shw_pwm_update_timeout(timeout);
	else shw_pwm_update_timeout(SHW_FAN_UPDATETO_DEFAULT);

	shw_pwm_speed(bmask,(speed)/100.0);
}


/**
 * Check all the LED accesible from the CPU.
 */
void led_tool(int argc, char **argv)
{
	char opt=0; int optind=1;
	int i, j;
	static unsigned char pattern1[] = {1,0,1,0};
	static unsigned char pattern2[] = {0,1,1,0};
	static char* desc[]={"green","orange","yellow","none"};
	uint8_t f_help=0, f_cpu=0, f_sfp=0, f_front=0, f_all=1;
	int spause=3, idpattern=-1;

	print_args(argc,argv);

	while ((opt = getopt(argc, argv, "p:i:csfh?")) != 0xFF)
	{
		optind++;
		switch (opt)
		{
		case 'p': spause=atoideflim(optarg,spause,0,0xFFFFFF); optind++; break;
		case 'i': idpattern=atoideflim(optarg,idpattern,-1,sizeof(pattern1)); optind++; break;
		case 'c': f_cpu=1; f_all=0; break;
		case 's': f_sfp=1; f_all=0; break;
		case 'f': f_front=1; f_all=0; break;
		case 'h':
		case '?':
			f_help=1;
			break;
		default:
			printf("find: illegal option %c (%x)\n", opt,opt);
			break;
		}
	}
	argc-=optind;
	argv+=optind;

	print_args(argc,argv);

	if(f_help)
	{
		printf("Usage %s %s [OPTIONS]\n",pgr_name,cmd_name);
		printf(""
				"OPTIONS:\n"
				"	-i id of the selected patter (-1=all)\n"
				"	-p pause during X sec between pattern\n"
				"	-c cpu led (inside box, on SCB)\n"
				"	-s SFP led\n"
				"	-f front led\n"
				"	-h show this help\n"
				""
		);
		return;
	}
	if(f_all)
	{
		f_cpu=1;
		f_sfp=1;
		f_front=1;
	}

	TRACE(TRACE_INFO,"Starting led test c=%d,s=%d,f=%d (p= %ds, id=%d)",f_cpu,f_sfp,f_front,spause,idpattern);

	//Mapping in userspace
	//shw_pio_mmap_init();

	//Init and configuration
	assert_init(shw_io_init());
	assert_init(shw_io_configure_all());

	//Initialization of FPGA LEDs.
	if(f_sfp)
	{
		shw_fpga_mmap_init();
		shw_sfp_buses_init();
		shw_sfp_gpio_init();
	}

	for(i=0; i<sizeof(pattern1);i++)
	{
		//Check only for desired pattern
		if(idpattern>=0 && idpattern!=i) continue;

		TRACE(TRACE_INFO, "#%d > %-8s (Link & Sync=%d, WR & Act=%d)", i,desc[i],pattern1[i],pattern2[i]);
		if(f_cpu)
		{
			shw_io_write(shw_io_led_cpu1,pattern2[i]);
			shw_io_write(shw_io_led_cpu2,pattern1[i]);
		}

		if(f_sfp)
		{

			for(j=0; j<18;j++)
			{
				//left LED
				shw_sfp_set_led_link(j,pattern1[i]);
				shw_sfp_set_led_wrmode(j,pattern2[i]);

				//Right LED
				shw_sfp_set_led_synced(j,pattern1[i]);
				//We can not access to the activity LED from the FPGA
			}
		}

		if(f_front)
		{
			shw_io_write(shw_io_led_state_g,pattern1[i]);
			shw_io_write(shw_io_led_state_o,pattern2[i]);
		}

		if(idpattern<0) sleep(spause);
	}
}

//#define SCB_32 1

#define TSENSORS_SYSMON 0
#define TSENSORS_I2C 1

#define Sensor_TEMP_FPGA	0x4A //(7bits addr) Below FPGA
#ifdef SCB_32
#define Sensor_TEMP_PLL 	0x4C //(7bits addr) PLLs
#define Sensor_TEMP_PSL 	0x49 //(7bits addr) Power supply left
#define Sensor_TEMP_PSR		0x4D //(7bits addr) Power supply right
#else
#define Sensor_TEMP_PLL 	0x48 //(7bits addr) PLLs
#define Sensor_TEMP_PSR		0x49 //(7bits addr) Power supply
#define Sensor_TEMP_PSL		0x49 //(7bits addr) Power supply
#endif


typedef struct
{
	char name[20];
	uint8_t type;
	uint32_t val;
} t_sensor;

t_sensor sensors[7] = {
		{"T_ACT",TSENSORS_SYSMON,1},
		{"T_MAX",TSENSORS_SYSMON,2},
		{"T_MIN",TSENSORS_SYSMON,3},
		{"T_BFPGA",TSENSORS_I2C,Sensor_TEMP_FPGA},
		{"T_PLL",TSENSORS_I2C,Sensor_TEMP_PLL},
		{"T_PSR",TSENSORS_I2C,Sensor_TEMP_PSR},
		{"T_PSL",TSENSORS_I2C,Sensor_TEMP_PSL},
};



/**
 * Monotorize basic behaviour of the switch (temperature, volatge)
 */
void monotorize(int argc, char *argv[])
{
	char opt=0;
	uint8_t f_time=0, f_help=0;
	int optind=1, maxloop=-1, iloop=0, nsecup=3;
	int _isensor, isensor, start=0,end=sizeof(sensors)/sizeof(t_sensor);
	float t_cur;

	print_args(argc,argv);

	while ((opt = getopt(argc, argv, "l:u:th?")) != 0xFF)
	{
		optind++;
		switch (opt)
		{
		case 'l': maxloop=atoideflim(optarg,-1,0,0x0FFFFFFF); optind++; break;
		case 'u': nsecup=atoideflim(optarg,-1,0,3600); optind++; break;
		case 't': f_time=1; break;
		case 'h':
		case '?':
			f_help=1;
			break;
		default:
			printf("find: illegal option %c (%x)\n", opt,opt);
			break;
		}
	}
	argc-=optind;
	argv+=optind;

	print_args(argc,argv);
	_isensor=atoideflim(argv[0],-1,0,end);

	if(f_help)
	{
		printf("Usage %s %s [OPTIONS]\n",pgr_name,cmd_name);
		printf(""
				"OPTIONS:\n"
				"	-l number of loop (if negative it loop infinitively)\n"
				"	-u update frequency in second (default=3s)\n"
				"	-t show time in seconds\n"
				""
		);
		return;
	}


	TRACE(TRACE_INFO,"Starting temp tool (%d,%d,%d)",_isensor,maxloop,end);

	//Initialization
	shw_pio_mmap_init();
	shw_fpga_mmap_init();
	shw_init_i2c_sensors();

	if(0 <= _isensor && _isensor < end)
	{
		start=_isensor;
		end=_isensor+1;
	}


	if(f_time) printf("%10s","time (in s)\t");

	//Print sensors columns
	for(isensor=start; isensor<end; isensor++)
	{
		printf("%6s\t",sensors[isensor].name);
		if(sensors[isensor].type==TSENSORS_I2C)
			tmp100_write_reg(sensors[isensor].val, 1, 0x60); // 12-bit resolution
	}
	printf("\n"); 	fflush(stdout);

	while(iloop++!=maxloop)
	{
		printf("\n");
		if(f_time) printf("%10d\t",time(NULL));

		for(isensor=start; isensor<end; isensor++)
		{
			if(sensors[isensor].type==TSENSORS_I2C)
			{
				t_cur=tmp100_read_temp(sensors[isensor].val);
			}
			else
			{
				t_cur=(float)((_fpga_readl(0x57000) >> sensors[isensor].val*8) & 0xFF)/2.0;
			}


			printf("%6.1f\t",t_cur);
		}


		fflush(stdout);
		sleep(nsecup);
	}
	printf("\n");
}





/**
 * Parse cmd line to display GPIO value.
 */
int gpio_tool(int argc, char **argv)
{

	int value;
	pio_pin_t gpio = {-1, -1, PIO_MODE_GPIO, PIO_OUT} ;

	print_args(argc,argv);

	gpio.port=(int)argv[1][0]-'A'+1;
	gpio.pin=atoideflim(argv[2],-1,0,32);
	value=atoideflim(argv[3],1,0,1);

	printf("%d => ", value);
	shw_pio_printf(&gpio);

	shw_pio_mmap_init();
	shw_pio_configure((const pio_pin_t *)&gpio);
	if(value) shw_pio_set((const pio_pin_t *)&gpio,1);
	else shw_pio_set((const pio_pin_t *)&gpio,0);
}


/**
 * Parse cmd line to display GPIO value.
 */
int io_tool(int argc, char **argv)
{
	int i=0, id=0, value=-1;
	char cmdchr='h';
	shw_io_t* all_io = (shw_io_t*)_all_shw_io;

	argc--; argv++;
	print_args(argc,argv);


	if(argc==1 && strcmp(argv[0],"-h")==0)
	{
		printf("Usage %s %s  [<id>] [<value>]\n",pgr_name,cmd_name);
		printf(""
				"%s %s             : list all available io\n"
				"%s %s 	<id>       : Read the IO with id\n"
				"%s %s 	<id> <val> : Write value in the IO with id\n"
				"%s %s 	-h         : Show this little help\n"
				"",pgr_name,cmd_name,pgr_name,cmd_name,pgr_name,cmd_name,pgr_name,cmd_name
		);
		return 1;
	}

    assert_init(shw_pio_mmap_init());
	assert_init(shw_io_init());
	assert_init(shw_io_configure_all());

	id=atoideflim(argv[0],id,0,NUM_SHW_IO_ID);
	value=atoidef(argv[1],value);

	//List
	if(argc==0)
	{
		TRACE(TRACE_INFO, "io list (%d)",NUM_SHW_IO_ID);
		for(i=0;i<NUM_SHW_IO_ID;i++)
		{
			if(all_io[i].type) printf("%d => %-20s %d\n",all_io[i].ID,all_io[i].name,all_io[i].type);
		}
	}
	else if(argc==1)
	{
		//Read
		printf("#%d %s = %d (0x%X)\n",all_io[id].ID,all_io[id].name,shw_io_read(id),shw_io_read(id));
	}
	else if(argc==2)
	{
		//Write
		printf("#%d %s writing %d (0x%X)\n",all_io[id].ID,all_io[id].name,value,value);
		shw_io_write(id,value);
	}



}




#define FPGA_WB_SYSMON_ADDRESS 0x57000

#include "i2c_sfp.h"
extern struct i2c_bus i2c_buses[];

void i2c_tool(int argc, char *argv[])
{

	char opt=0;
	int optind=1;
	uint8_t f_time=0, f_help=0;
	int i=0, nbus=0, addr=0;
	char cmdchr='h';
	uint8_t value, map[32];

	print_args(argc,argv);

	while ((opt = getopt(argc, argv, "b:a:h?")) != 0xFF)
	{
		optind++;
		switch (opt)
		{
		case 'b': nbus=atoideflim(optarg,nbus,0,6); optind++; break;
		case 'a': addr=atoideflim(optarg,addr,0,128); optind++; break;
		case 'h':
		case '?':
			f_help=1;
			break;
		default:
			printf("find: illegal option %c (%x)\n", opt,opt);
			break;
		}
	}
	argc-=optind;
	argv+=optind;

	print_args(argc,argv);

	cmdchr=(argc>=1)?argv[0][0]:'h';
	value=atoideflim(argv[1],0,0,0xFF);

	if(f_help || cmdchr=='h')
	{
		printf("Usage %s %s [OPTIONS] <cmd> [<value>]\n",pgr_name,cmd_name);
		printf(""
				"cmd: r (read), w (write), s (scan), c (config), h (help)\n"
				"OPTIONS:\n"
				"	-b Bus number\n"
				"	-a Adress in the bus (0x20+addr)\n"
				""
		);
		return;
	}


	TRACE(TRACE_INFO, "i2c scanning");

	//Initialization
	shw_pio_mmap_init();
	shw_i2c_io_init();
	shw_fpga_mmap_init();
	shw_sfp_buses_init();

	//Scanning
	if(cmdchr=='s')
	{
		i=0;
		while(1)
		{
			printf("bus: %i ",i);
			fflush(stdout);
			if(shw_sfp_bus_scan(i,map)<0) break;
			else i++;
		}
		shw_i2c_io_scan(map);
	}
}



/**
 * Similar as using as devmem but without using the 0x100XXXXX offset
 * It also improve how parsing is done.
 */
void wb_devmem(int argc, char *argv[])
{
	char opt=0;
	uint8_t help=0,f_u=0, f_x=0, f_i=0, f_s=0, f_c=0;
	uint32_t addr, value, optind=1;

	print_args(argc,argv);


	char *p_c;
	uint8_t *p_u8;
	uint16_t *p_u16;
	int16_t *p_i16;

	while ((opt = getopt(argc, argv, "uxisc8h?")) != 0xFF)
	{
		optind++;
		switch (opt)
		{
		case 'u': f_u=1; break;
		case 'x': f_x=1; break;
		case 'i': f_i=1; break;
		case 's': f_s=1; break;
		case '8':
		case 'c': f_c=1; break;
		case 'h':
		case '?':
			help=1;
			break;
		default:
			printf("find: illegal option %c (%x)\n", opt,opt);
			break;
		}
	}
	argc-=optind;
	argv+=optind;



	if(help || argc<=0)
	{
		printf("Usage %s %s [OPTIONS] <addr> [<value>]\n",pgr_name,cmd_name);
		printf(""
				"   addr = Wishbone address (0x30034)\n"
				"   OPTIONS: (for read) unsigned: -u, hexa: -x, int: -i,  2x short: -s, 4x 8 bits: -c\n");
		return;
	}


	addr=strtol(argv[0],NULL,16); //Must be in hexadecimal format
	print_args(argc,argv);

	shw_fpga_mmap_init();

	//Just read
	if(argc==1)
	{
		printf("R@0x%08x > ",addr);
		value=_fpga_readl(addr);
		if(f_x) printf("0x%x, ",value);
		if(f_i)
		{
			if(f_u) printf("%d, ",value);
			else printf("%d, ",(int)value);
		}
		if(f_s)
		{
			if(f_u)
			{
				p_u16=(uint16_t*)&value;
				printf("%d %d, ",p_u16[0],p_u16[1]);
			}
			else
			{
				p_i16=(int16_t*)&value;
				printf("%d %d, ",p_i16[0],p_i16[1]);
			}
		}
		if(f_c)
		{
			fflush(stdout);
			if(f_u)
			{
				p_u8=(uint8_t*)&value;
				printf("%d %d %d %d, ",p_u8[0],p_u8[1],p_u8[2],p_u8[3]);
			}
			else
			{
				p_c=(int8_t*)&value;
				printf("%c %c %c %c, ",p_c[0],p_c[1],p_c[2],p_c[3]);
			}
		}
		printf("\n");
	}
	else
	{
		TRACE(TRACE_FATAL, "Write Not implemented");
	}
}


int test(int argc, char *argv[])
{

	/* Map CPU's pin into memory space */
	assert_init(shw_pio_mmap_init());

	shw_io_init();


	//	shw_i2c_io_test();
	//
	TRACE(TRACE_INFO,"ver=%f %d %s", shw_get_hw_ver(),shw_get_fpga_type(),get_shw_info('f'));

	//	struct i2c_bus* iobus=shw_i2c_io_get_bus();
	//	i2c_read(iobus,0x0,0x5,1);



}

void usage_error()
{
	fprintf(stderr, "usage: %s <command> [args]\n", pgr_name);
	fprintf(stderr,"available commands are:\n"
			"   --fan <fan_speed> (from 0 to 100)\n"
			"   --mon\n"
			"   --wb\n"
			"   --led <test_ID>\n"
			"   --gpio <A,B,C,...> <number> <status>\n"
			"\n");
	exit(1);
}


int main(int argc, char **argv)
{

	trace_log_stderr();

	pgr_name=*(argv++); argc--;
	if (argc == 0) usage_error();

	cmd_name=argv[0];

	TRACE(TRACE_INFO,"(%s %s)",__DATE__,__TIME__);

	if (strcmp(cmd_name, "--fan") == 0) {
		fan_tool(argc,argv);
	}
	else if (strcmp(cmd_name, "--led") == 0) {
		led_tool(argc,argv);
	}
	else if (strcmp(cmd_name, "--gpio") == 0)
	{
		gpio_tool(argc,argv);
	}
	else if (strcmp(cmd_name, "--mon") == 0)
	{
		monotorize(argc,argv);
	}
	else if (strcmp(cmd_name, "--io") == 0)
	{
		io_tool(argc,argv);
	}
	else if (strcmp(cmd_name, "--i2c") == 0)
	{
		i2c_tool(argc,argv);
	}
	else if (strcmp(cmd_name, "--wb") == 0)
	{
		wb_devmem(argc,argv);
	}
	else if (strcmp(cmd_name, "--test") == 0)
	{
		test(argc,argv);
	}
	else if (strcmp(cmd_name, "--") == 0) {
		printf("Not implemented");
		usage_error();
	}
	else {
		printf("Command: '%s' is unknown",cmd_name);
		usage_error();
	}
	return 0;
}


