/*
 * 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"

#define INPUT_PRESENT_FILE "/sys/bus/i2c/drivers/si5341/7-0076/input_present"
#define PLL_LOCKED_FILE    "/sys/bus/i2c/drivers/si5341/7-0076/pll_locked"
#define INPUT_SELECTED_FILE "/sys/bus/i2c/drivers/si5341/7-0076/input_selected"
#define SI5341_PREFIX_PATH "/sys/kernel/debug/clk/si5341"
#define SI5341_N_CH_OUT    10

int get_file_content(char *file_name, int *val)
{
    int ret;
    FILE * fp;

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

    ret = fscanf(fp, "%d", val);
    fclose(fp);
    if (ret <= 0 || ret == EOF) {
	diot_error_add("Unable to read value from file %s\n", file_name);
	return -1;
    }

    return ret;
}

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

    sprintf(input_name, "error");
    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;
    }

    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;
    }

    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;
    }

    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(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(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(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 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(INPUT_PRESENT_FILE, &val);
    printf("Input present   |%11s |\n", check_ret_value(ret, val));
    ret = get_file_content(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;
}
