/*
 * diot_util - diagnostic tool for DIOT system board
 *
 * Functions to handle clocks
 *
 * Author: Adam Wujek for CERN
 * Copyright CERN 2021
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include "diot_util_error.h"
#include "diot_util_pts.h"
#include "diot_util_i2c.h"
#include "util.h"

# define INPUT_PRESENT_FILE  "/sys/bus/i2c/drivers/si5341/" SI5341_I2C "-00" SI5341_I2C_ADDR "/input_present"
# define PLL_LOCKED_FILE     "/sys/bus/i2c/drivers/si5341/" SI5341_I2C "-00" SI5341_I2C_ADDR "/pll_locked"
# define INPUT_SELECTED_FILE "/sys/bus/i2c/drivers/si5341/" SI5341_I2C "-00" SI5341_I2C_ADDR "/input_selected"



#define SI5341_PREFIX_PATH "/sys/kernel/debug/clk/si5341"
#define SI5341_N_CH_OUT    10


int get_selected_input(int *val, char *input_name, char *input_name_dtb)
{
    int ret = 0;
    FILE * fp;
    char *file_name = INPUT_SELECTED_FILE;

    if (input_name)
    	sprintf(input_name, "error");
    if (input_name_dtb)
    	sprintf(input_name_dtb, "error");

    fp = fopen (file_name, "r");
    if (!fp) {
	diot_error_add("Unable to read file %s\n", file_name);
	*val = -1;
	return -1;
    }

    if (val) {
    	ret = fscanf(fp, "%d", val);
    	if (ret <= 0 || ret == EOF) {
    	    diot_error_add("Unable to read clock's input id from file %s\n",
    	    	       file_name);
    	    goto out;
    	}
    }

    if (input_name) {
    	ret = fscanf(fp, "%s", input_name);
    	if (ret <= 0 || ret == EOF) {
    	    diot_error_add("Unable to read clock's input name from file %s\n",
    	    	       file_name);
    	    goto out;
    	}
    }

    if (input_name_dtb) {
    	ret = fscanf(fp, "%s", input_name_dtb);
    	if (ret <= 0 || ret == EOF) {
    	    diot_error_add("Unable to read clock's input name as in DTB from file"
    	    	       " %s\n", file_name);
    	    goto out;
    	}
    }

out:
    fclose(fp);

    return ret;
}

char *value_float_or_error(char *buff, int ret, int val)
{
    if (ret < 0)
	return "error";

    sprintf(buff, "%.3f", ((float)val)/1000000);
    return buff;
}

char *value_or_error(char *buff, int ret, int val)
{
    if (ret < 0)
	return "error";

    sprintf(buff, "%d", val);
    return buff;
}

void print_clk_rate(void)
{
    int val;
    int ret;
    int i;
    char path_buff[100];
    char buff[20];

    printf("-------+------------+\n");
    printf("clk    |        MHz |\n");
    printf("-------+------------+\n");

    snprintf(path_buff, sizeof(path_buff), "%s/clk_rate", SI5341_PREFIX_PATH);
    ret = get_file_content_int(path_buff, &val);
    printf("InDiv  | %10s |\n", value_float_or_error(buff, ret, val));

    snprintf(path_buff, sizeof(path_buff), "%s.N0/clk_rate",
	     SI5341_PREFIX_PATH);
    ret = get_file_content_int(path_buff, &val);
    printf("PLLout | %10s |\n", value_float_or_error(buff, ret, val));

    for (i = 0; i < SI5341_N_CH_OUT; i++) {
	snprintf(path_buff, sizeof(path_buff), "%s.%d/clk_rate",
		 SI5341_PREFIX_PATH, i);
	ret = get_file_content_int(path_buff, &val);
	printf("Out%d   | %10s |\n", i, value_float_or_error(buff, ret, val));
    }

    printf("-------+------------+\n");
}


char *check_ret_value(int ret, int val)
{
    if (ret < 0)
	return "error";
    if (val == 1)
	return "true";
    if (val == 0)
	return "false";

    return "Unkn";
}

int pts_clocks(void)
{
	struct pts_sensor *pts_sensor_p;
	int val, ret;
	int errors = 0;
	int i;
	char path_buff[100];

	printf("Test Clocks");

	ret = get_file_content_int(INPUT_PRESENT_FILE, &val);
	pts_sensor_p = find_pts_sensor("PLL Input Present");
	if (ret < 0 || !pts_sensor_inrange(val, pts_sensor_p)) {
		if (errors++ == 0)
			printf("\rFAIL   Clocks\n");
		printf("\t%s: ERROR (%d vs [%.1f;%.1f])\n", pts_sensor_p->short_name,
				val, pts_sensor_p->allowed_min, pts_sensor_p->allowed_max);
	}

	ret = get_file_content_int(PLL_LOCKED_FILE, &val);
	pts_sensor_p = find_pts_sensor("PLL Locked");
	if (ret < 0 || !pts_sensor_inrange(val, pts_sensor_p)) {
		if (errors++ == 0)
			printf("\rFAIL   Clocks\n");
		printf("\t%s: ERROR (%d vs [%.1f;%.1f])\n", pts_sensor_p->short_name,
				val, pts_sensor_p->allowed_min, pts_sensor_p->allowed_max);
	}

	ret = get_selected_input(&val, NULL, NULL);
	pts_sensor_p = find_pts_sensor("PLL Input");
	if (ret < 0 || !pts_sensor_inrange(val, pts_sensor_p)) {
		if (errors++ == 0)
			printf("\rFAIL   Clocks\n");
		printf("\t%s: ERROR (%d vs [%.1f;%.1f])\n", pts_sensor_p->short_name,
				val, pts_sensor_p->allowed_min, pts_sensor_p->allowed_max);
	}

	for (i = 0; i < SI5341_N_CH_OUT; i++) {
		snprintf(path_buff, sizeof(path_buff), "%s.%d/clk_rate",
				SI5341_PREFIX_PATH, i);
		ret = get_file_content_int(path_buff, &val);
		snprintf(path_buff, sizeof(path_buff), "PLL Out%d", i);
		pts_sensor_p = find_pts_sensor(path_buff);
		if (ret < 0 || !pts_sensor_inrange((float)val/1000000, pts_sensor_p)) {
			if (errors++ == 0)
				printf("\rFAIL   Clocks\n");
			printf("\t%s: ERROR (%.1f vs [%.1f;%.1f])\n", pts_sensor_p->short_name,
					(float)val/1000000, pts_sensor_p->allowed_min, pts_sensor_p->allowed_max);
		}
	}
	

	if (errors == 0) {
		printf("\rOK     Clocks\n");
	}

	return errors;
}

int cmd_clocks(char *params)
{
    int val;
    int ret;
    char buff[20];
    char buff_input_name[20];
    char buff_input_name_DTB[20];

    diot_error_clear();

    printf("----------------+------------+\n");
    ret = get_file_content_int(INPUT_PRESENT_FILE, &val);
    printf("Input present   |%11s |\n", check_ret_value(ret, val));
    ret = get_file_content_int(PLL_LOCKED_FILE, &val);
    printf("Pll locked      |%11s |\n", check_ret_value(ret, val));
    get_selected_input(&val, buff_input_name, buff_input_name_DTB);
    printf("Input id        |%11s |\n", value_or_error(buff, val, val));
    printf("Input name      |%11s |\n", buff_input_name);
    printf("Input name(DTB) |%11s |\n", buff_input_name_DTB);
    printf("----------------+------------+\n");
    printf("\n");

    print_clk_rate();
    printf("\n");

    diot_error_print();

    return 0;
}
