/*
 * University of Illinois/NCSA Open Source License
 *
 * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved.
 *
 */

#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>

#include <e_smi/e_smi.h>

#define RED "\x1b[31m"
#define MAG "\x1b[35m"
#define RESET "\x1b[0m"
#include <e_smi/e_smi64Config.h>

#define ARGS_MAX 64
#define SHOWLINESZ 256

/*
 * To handle multiple errors while reporting tool summary.
 * 1234 is just chosen to make this value different than other error codes
 */
#define ESMI_MULTI_ERROR	1234
#define COLS			3
#define AID_COUNT		4
#define XCC_COUNT		8
#define NUM_XGMI_LINKS		8
#define MAX_SVI3_RAIL_SELECTION  0x1
#define MAX_SVI3_RAIL_INDEX  0x7//[30:28]=SVI3_Rail_index
#define MAX_SPD_DIMM_ADDRESS 0xFF//[7:0]=DIMM_ADDRESS
#define MAX_SPD_LID_ADDRESS  0xF//[11:8]=LID of device
#define MAX_SPD_REG_OFFSET   0x7FF//[22:12]=Register offset in a given register space
#define MAX_SPD_REG_SPACE    0x1//[23]=Register space
#define MAX_SPD_WRITE_DATA   0xFF//[31:24]=Write Data
#define MAX_POWER_EFFICIENCY_MODE 0x7//[2:0]=Currentefficiency mode.
#define MAX_POWER_EFFICIENCY_UTIL 0x7F//[9:3]=Utilization point for balanced core modes (%).
#define MAX_POWER_EFFICIENCY_PPTLIMIT 0x1FFFFF//[30:10]=PPT limit for balanced core modes(mW).
#define MAX_SDPSLIMIT 0x7FFFFFFF//[30:00]=SDPS limit(mW).
#define POWER_EFFICIENCY_MODE_4 0x4
#define POWER_EFFICIENCY_MODE_5 0x5
#define MAX_RANGE_0xFF 0xFF
#define DIMM_POW_MULT_FACTOR 20
#define METRICS_TABLE_COLLECTION_SLEEP_TIME 100

/* Total bits required to represent integer */
#define NUM_OF_32BITS		(sizeof(uint32_t) * 8)
#define NUM_OF_64BITS		(sizeof(uint64_t) * 8)
#define KILO			pow(10,3)

static const char *bw_string[3] = {"aggregate", "read", "write"}; //!< bandwidth types for io/xgmi links

static int flag;
static double initial_delay_in_secs = 0;
static double loop_delay_in_secs    = 0;
static bool loop_delay_in_secs_passed = false;
static int loop_count		    = 0;
static char* log_file_name   = NULL;
static char* stoploop_file_name   = NULL;
static char* log_file_header = NULL;
static char* log_file_data   = NULL;
static bool create_log_header = false;
static char* startmetric_file_name  = NULL;
static char* stopmetric_file_name   = NULL;

static double metric_table_delay_in_secs = 0;
static bool show_metric_table[MAX_SOCKET] = {false, false, false, false, false, false, false, false};
static double metric_table_default_delay_in_ms = METRICS_TABLE_COLLECTION_SLEEP_TIME;

typedef enum {
	DONT_PRINT_RESULTS = 0,
	PRINT_RESULTS,
	PRINT_RESULTS_AS_CSV,
	PRINT_RESULTS_AS_JSON
}print_results_t;
static print_results_t print_results = PRINT_RESULTS;

typedef enum {
	RAW_METRIC_TABLE_FORMAT = 0,
	HUMAN_READABLE_METRIC_TABLE_FORMAT = 1
}metric_table_format_t;
static metric_table_format_t metric_table_format = HUMAN_READABLE_METRIC_TABLE_FORMAT;

typedef enum {
	DONT_LOG_TO_FILE = 0,
	LOG_TO_FILE
}log_to_file_t;
static log_to_file_t log_to_file = DONT_LOG_TO_FILE;

static struct epyc_sys_info {
	uint32_t sockets;
	uint32_t cpus;
	uint32_t threads_per_core;
	uint32_t family;
	uint32_t model;
	void (*show_addon_cpu_metrics)(uint32_t *);
	void (*show_addon_socket_metrics)(uint32_t *, char **);
	void (*show_addon_clock_metrics)(uint32_t *, char **);
} sys_info;

enum floorlimit_type {
	FLOOR_FREQUENCY = 00,
	EFF_FLOOR_FREQUENCY = 01,
};

static void sleep_in_milliseconds(unsigned milliseconds)
{
	usleep(milliseconds * 1000); // takes microseconds
}

void toUpperCaseString(char *str)
{
	if (str == NULL) return; // Handle null input

	while (*str)
	{
		*str = toupper(*str); // Convert each character to uppercase
		str++;
	}
}

void append_string(char **buffer, const char *new_str) {
	size_t current_length = *buffer ? strlen(*buffer) : 0;
	size_t new_str_length = strlen(new_str);
	size_t new_length = current_length + new_str_length + 1; // +1 for null terminator

	// Reallocate buffer to the new size
	char *new_buffer = realloc(*buffer, new_length);
	if (new_buffer == NULL) {
		return;
	}

	// Append the new string
	strcpy(new_buffer + current_length, new_str);

	// Update the buffer pointer
	*buffer = new_buffer;
}

static void print_socket_footer()
{
	if(print_results != PRINT_RESULTS) return;
	int i;

	printf("\n----------------------------------");
	for (i = 0; i < sys_info.sockets; i++) {
		printf("-------------------");
	}
}

static void print_socket_header()
{
	if(print_results != PRINT_RESULTS) return;
	int i;

	print_socket_footer();
	printf("\n| Sensor Name\t\t\t |");
	for (i = 0; i < sys_info.sockets; i++) {
		printf(" Socket %-10d|", i);
	}
	print_socket_footer();
}

static void err_bits_print(uint32_t err_bits)
{
	if(print_results != PRINT_RESULTS) return;
	int i;

	printf("\n");
	for (i = 1; i < 32; i++) {
		if (err_bits & (1 << i))
			printf(RED "Err[%d]: %s\n" RESET, i, esmi_get_err_msg(i));
	}
}

#define ALLOWLIST_FILE "/dev/cpu/msr_allowlist"
static char *allowlistcontent = "# MSR # Write Mask # Comment\n"
			  "0xC0010299 0x0000000000000000\n"
			  "0xC001029A 0x0000000000000000\n"
			  "0xC001029B 0x0000000000000000\n"
			  "0xC00102F0 0x0000000000000000\n"
			  "0xC00102F1 0x0000000000000000\n";

static int write_msr_allowlist_file()
{
	int fd;
	int ret;

	if (!access(ALLOWLIST_FILE, F_OK)) {
		fd = open(ALLOWLIST_FILE, O_RDWR);
		if (fd < 0) {
			printf("Error in opening msr allowlist: %s\n", strerror(errno));
			return errno;
		}
		ret = write(fd, allowlistcontent, strlen(allowlistcontent));
		if (ret < 0) {
			printf("Error in writing msr allowlist: %s\n", strerror(errno));
			return errno;
		}

		close(fd);
	}
	printf("Successfully added msr allowlist.\n");

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_coreenergy(uint32_t core_id)
{
	esmi_status_t ret;
	uint64_t core_input = 0;

	ret = esmi_core_energy_get(core_id, &core_input);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get core[%d] energy, Err[%d]: %s\n",
			core_id, ret, esmi_get_err_msg(ret));
		if (ret == ESMI_PERMISSION) {
			printf(RED "\nTry adding msr allowlist using "
				"--writemsrallowlist tool option.\n" RESET);
		}
		return ret;
	}

	if(print_results == PRINT_RESULTS) {
		printf("-------------------------------------------------");
		printf("\n| core[%d] energy  | %17.3lf Joules \t|\n",
			core_id, (double)core_input/1000000);
		printf("-------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,CoreEnergy(Joules)\n%d,%.3f\n", core_id, (double)core_input/1000000);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"CoreEnergy(Joules)\":%.3f\n\t},", core_id, (double)core_input/1000000);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Core:%d CoreEnergy(Joules),", core_id);
			append_string(&log_file_header, temp_string);
		}
		sprintf(temp_string, "%.3f,", (double)core_input/1000000);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static int epyc_get_sockenergy(void)
{
	esmi_status_t ret;
	uint32_t i;
	uint64_t pkg_input = 0;
	uint32_t err_bits = 0;

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| Energy (K Joules)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketEnergy(KJoules),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_energy_get(i, &pkg_input);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3lf|", (double)pkg_input/1000000000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketEnergy(KJoules)\n%d,%.3f\n", i, (double)pkg_input/1000000000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketEnergy(KJoules)\":%.3f\n\t},", i, (double)pkg_input/1000000000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)pkg_input/1000000000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketEnergy(KJoules)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketEnergy(KJoules)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	print_socket_footer();
	if(print_results == PRINT_RESULTS)
		printf("\n");
	err_bits_print(err_bits);

	if ((err_bits >> ESMI_PERMISSION) & 0x1)
		printf(RED "\nTry adding msr allowlist using "
			"--writemsrallowlist tool option.\n" RESET);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

static void ddr_bw_get(uint32_t *err_bits)
{
	struct ddr_bw_metrics ddr;
	char bw_str[SHOWLINESZ] = {};
	char pct_str[SHOWLINESZ] = {};
	char max_str[SHOWLINESZ] = {};
	uint32_t bw_len;
	uint32_t pct_len;
	uint32_t i, ret;
	uint32_t max_len;

	if(print_results == PRINT_RESULTS)
		printf("\n| DDR Bandwidth\t\t\t |");

	snprintf(max_str, SHOWLINESZ, "\n| \tDDR Max BW (GB/s)\t |");
	snprintf(bw_str, SHOWLINESZ, "\n| \tDDR Utilized BW (GB/s)\t |");
	snprintf(pct_str, SHOWLINESZ, "\n| \tDDR Utilized Percent(%%)\t |");
	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d DDRMaxBw(GB/s),Socket:%d DDRUtilizedBw(GB/s),Socket:%d DDRUtilizedPercent(%%),", i, i, i);
			append_string(&log_file_header, temp_string);
		}

		if(print_results == PRINT_RESULTS)
			printf("                  |");

		ret = esmi_ddr_bw_get(i, &ddr);
		bw_len = strlen(bw_str);
		pct_len = strlen(pct_str);
		max_len = strlen(max_str);
		if(!ret) {
			snprintf(max_str + max_len, SHOWLINESZ - max_len, " %-17d|", ddr.max_bw);
			snprintf(bw_str + bw_len, SHOWLINESZ - bw_len, " %-17d|", ddr.utilized_bw);
			snprintf(pct_str + pct_len, SHOWLINESZ - pct_len, " %-17d|", ddr.utilized_pct);
			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,DDRMaxBw(GB/s),DDRUtilizedBw(GB/s),DDRUtilizedPercent(%%)\n%d,%d,%d,%d\n", i, ddr.max_bw, ddr.utilized_bw, ddr.utilized_pct);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DDRMaxBw(GB/s)\":%d,\n\t\t\"DDRUtilizedBw(GB/s)\":%d,\n\t\t\"DDRUtilizedPercent(%%)\":%d\n\t},", i, ddr.max_bw, ddr.utilized_bw, ddr.utilized_pct);

			if(log_to_file) {
				sprintf(temp_string, "%d,%d,%d,", ddr.max_bw, ddr.utilized_bw, ddr.utilized_pct);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			snprintf(max_str + max_len, SHOWLINESZ - max_len, " NA (Err: %-2d)     |", ret);
			snprintf(bw_str + bw_len, SHOWLINESZ - bw_len, " NA (Err: %-2d)     |", ret);
			snprintf(pct_str + pct_len, SHOWLINESZ - pct_len, " NA (Err: %-2d)     |", ret);
			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,DDRMaxBw(GB/s),DDRUtilizedBw(GB/s),DDRUtilizedPercent(%%)\n%d,NA (Err:%d),NA (Err:%d),NA (Err:%d)\n", i, ddr.max_bw, ddr.utilized_bw, ddr.utilized_pct);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DDRMaxBw(GB/s)\":\"NA (Err:%d)\",\n\t\t\"DDRUtilizedBw(GB/s)\":\"NA (Err:%d)\"\n\t\t\"DDRUtilizedPercent(%%)\":\"NA (Err:%d)\"\n\t},", i, ret, ret, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),NA (Err:%d),NA (Err:%d),", ret, ret, ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	if(print_results == PRINT_RESULTS)
	{
		printf("%s", max_str);
		printf("%s", bw_str);
		printf("%s", pct_str);
	}
}

static int epyc_get_ddr_bw(void)
{
	uint32_t err_bits = 0;

	print_socket_header();
	ddr_bw_get(&err_bits);

	print_socket_footer();
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

static int epyc_get_temperature(void)
{
	esmi_status_t ret;
	uint32_t i;
	uint32_t tmon = 0;
	uint32_t err_bits = 0;

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| Temperature\t\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d Temperature('C),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_temperature_get(i, &tmon);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %3.3f'C\t    |", (double)tmon/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Temperature('C)\n%d,%.3f\n", i, (double)tmon/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Temperature('C)\":%.3f\n\t},", i, (double)tmon/1000);

			if(log_to_file) {
				sprintf(temp_string, "%3.3f,", (double)tmon/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Temperature('C)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Temperature('C)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),",ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	print_socket_footer();
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_smu_fw_version(void)
{
	struct smu_fw_version smu_fw;
	esmi_status_t ret;

	ret = esmi_smu_fw_version_get(&smu_fw);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get SMU Firmware Version, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("\n--------------------------------------------------");
		printf("\n| SMU FW Version   |  %u.%u.%u \t\t |\n",
			smu_fw.major, smu_fw.minor, smu_fw.debug);
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,SMUFWversion\n0,%u.%u.%u\n", smu_fw.major, smu_fw.minor, smu_fw.debug);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":0,\n\t\t\"SMUFWVersion\":\"%u.%u.%u\"\n\t},", smu_fw.major, smu_fw.minor, smu_fw.debug);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Socket:0 SMUFWVersion,");
			append_string(&log_file_header, temp_string);
		}

		sprintf(temp_string, "%u.%u.%u,", smu_fw.major, smu_fw.minor, smu_fw.debug);
		append_string(&log_file_data, temp_string);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_hsmp_driver_version(void)
{
        struct hsmp_driver_version hsmp_driver_ver;
        esmi_status_t ret;

        ret = esmi_hsmp_driver_version_get(&hsmp_driver_ver);
        if (ret != ESMI_SUCCESS) {
                printf("Failed to get HSMP Driver Version, Err[%d]: %s\n",
                        ret, esmi_get_err_msg(ret));
                return ret;
        }
	if(print_results == PRINT_RESULTS)
	{
			printf("\n------------------------------------------");
			printf("\n| HSMP Driver Version   |  %u.%u \t\t |\n",
					hsmp_driver_ver.major, hsmp_driver_ver.minor);
			printf("------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,HSMPDriverVersion\n0,%u.%u\n", hsmp_driver_ver.major, hsmp_driver_ver.minor);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":0,\n\t\t\"HSMPDriverVersion\":%u.%u\n\t},", hsmp_driver_ver.major, hsmp_driver_ver.minor);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Socket:0 HsmpDriverVersion,");
			append_string(&log_file_header, temp_string);
		}

		sprintf(temp_string, "%u.%u,", hsmp_driver_ver.major, hsmp_driver_ver.minor);
		append_string(&log_file_data, temp_string);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_hsmp_proto_version(void)
{
	uint32_t hsmp_proto_ver;
	esmi_status_t ret;

	ret = esmi_hsmp_proto_ver_get(&hsmp_proto_ver);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get hsmp protocol version, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(print_results == PRINT_RESULTS)
	{
		printf("\n---------------------------------");
		printf("\n| HSMP Protocol Version  | %u\t|\n", hsmp_proto_ver);
		printf("---------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,HSMPProtocolVersion\n0,%u\n", hsmp_proto_ver);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":0,\n\t\t\"HsmpProtocolVersion\":%u\n\t},", hsmp_proto_ver);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Socket:0 HsmpProtocolVersion,");
			append_string(&log_file_header, temp_string);
		}

		sprintf(temp_string, "%u,", hsmp_proto_ver);
		append_string(&log_file_data, temp_string);
	}

	return ESMI_SUCCESS;
}

static int epyc_get_prochot_status(void)
{
	esmi_status_t ret;
	uint32_t i;
	uint32_t prochot;
	uint32_t err_bits = 0;

	print_socket_header();

	if(print_results == PRINT_RESULTS)
		printf("\n| ProchotStatus:\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d ProchotStatus,", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_prochot_status_get(i, &prochot);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17s|", prochot? "active" : "inactive");
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,ProchotStatus\n%d,%s\n", i, prochot? "active" : "inactive");
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"ProchotStatus\":%s\n\t},", i, prochot? "\"active\"" : "\"inactive\"");

			if(log_to_file) {
				sprintf(temp_string, "%s,", prochot? "active" : "inactive");
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,ProchotStatus\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"ProchotStatus\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	print_socket_footer();
	if(print_results == PRINT_RESULTS) printf("\n");
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;
	return ESMI_SUCCESS;
}
static bool print_src = false;

static void display_freq_limit_src_names(char **freq_src)
{
	int j = 0;
	int i;

	for (i = 0; i < sys_info.sockets; i++) {
		j = 0;
		char temp_string[300];
		if(print_results == PRINT_RESULTS)
			printf("*%d Frequency limit source names: \n", i);
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("Socket,FrequencyLimitSource\n%d,",i);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"FrequencyLimitSource\":\"", i);

		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d FrequencyLimitSource,", i);
			append_string(&log_file_header, temp_string);
		}

		while (freq_src[j + (i * ARRAY_SIZE(freqlimitsrcnames))]) {
			if(print_results == PRINT_RESULTS)
				printf(" %s\n", freq_src[j +(i * ARRAY_SIZE(freqlimitsrcnames))]);
			else if((print_results == PRINT_RESULTS_AS_CSV) || (print_results == PRINT_RESULTS_AS_JSON))
				printf("%s:", freq_src[j +(i * ARRAY_SIZE(freqlimitsrcnames))]);

			if(log_to_file)
			{
				sprintf(temp_string, "%s:", freq_src[j +(i * ARRAY_SIZE(freqlimitsrcnames))]);
				append_string(&log_file_data, temp_string);
			}
			j++;
		}
		if (j == 0)
		{
			if(print_results == PRINT_RESULTS)
				printf(" %s\n", "Reserved");
			else if((print_results == PRINT_RESULTS_AS_CSV) || (print_results == PRINT_RESULTS_AS_JSON))
				printf("%s:", "Reserved");

			if(log_to_file)
			{
				sprintf(temp_string, "%s:", "Reserved");
				append_string(&log_file_data, temp_string);
			}
		}
		if((print_results == PRINT_RESULTS) || (print_results == PRINT_RESULTS_AS_CSV))
			printf("\n");
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\"\n\t},");

		if(log_to_file)
		{
			sprintf(temp_string, "%s", ",");
			append_string(&log_file_data, temp_string);
		}
	}

}

static void get_sock_freq_limit(uint32_t *err_bits, char **freq_src)
{
	char str1[SHOWLINESZ];
	char str2[SHOWLINESZ];
	int len2, len1;
	uint16_t limit;
	int ret;
	int i;
	int size = ARRAY_SIZE(freqlimitsrcnames);

	if(print_results == PRINT_RESULTS)
		printf("\n| Current Active Freq limit\t |");

	snprintf(str1, SHOWLINESZ, "\n| \t Freq limit (MHz) \t |");
	snprintf(str2, SHOWLINESZ, "\n| \t Freq limit source \t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d ActiveFrequencyLimit(MHz),", i);
			append_string(&log_file_header, temp_string);
		}

		len1 = strlen(str1);
		len2 = strlen(str2);
		if(print_results == PRINT_RESULTS) printf("                  |");
		ret = esmi_socket_current_active_freq_limit_get(i, &limit, freq_src + (i * size));
		if (!ret) {
			snprintf(str1 + len1, SHOWLINESZ - len1, " %-17u|", limit);
			snprintf(str2 + len2, SHOWLINESZ - len2, " Refer below[*%d]  |", i);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,ActiveFrequencyLimit(MHz)\n%d,%u\n", i, limit);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"ActiveFrequencyLimit(MHz)\":%u\n\t},", i, limit);

			if(log_to_file)
			{
				sprintf(temp_string, "%u,", limit);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			snprintf(str1 + len1, SHOWLINESZ - len1, " NA (Err: %-2d)     |", ret);
			snprintf(str2 + len2, SHOWLINESZ - len2, " NA (Err: %-2d)     |", ret);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,ActiveFrequencyLimit(MHz)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"ActiveFrequencyLimit(MHz)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file)
			{
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	if(print_results == PRINT_RESULTS)
	{
		printf("%s", str1);
		printf("%s", str2);
	}
	print_src = true;
}

static void get_sock_freq_range(uint32_t *err_bits)
{
	char str1[SHOWLINESZ];
	char str2[SHOWLINESZ];
	int len2, len1;
	uint16_t fmax;
	uint16_t fmin;
	int ret, i;

	if(print_results == PRINT_RESULTS)
		printf("\n| Socket frequency range\t |");

	snprintf(str1, SHOWLINESZ, "\n| \t Fmax (MHz)\t\t |");
	snprintf(str2, SHOWLINESZ, "\n| \t Fmin (MHz)\t\t |");
	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d Fmax(MHz),Socket:%d Fmin(MHz),", i, i);
			append_string(&log_file_header, temp_string);
		}

		if(print_results == PRINT_RESULTS) printf("                  |");
		len1 = strlen(str1);
		len2 = strlen(str2);
		ret = esmi_socket_freq_range_get(i, &fmax, &fmin);
		if (!ret) {
			snprintf(str1 + len1, SHOWLINESZ -len1, " %-17u|", fmax);
			snprintf(str2 + len2, SHOWLINESZ - len2 , " %-17u|", fmin);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Fmax(MHz),Fmin(MHz)\n%d,%u,%u\n", i, fmax, fmin);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Fmax(MHz)\":%u,\n\t\t\"Fmin(MHz)\":%u\n\t},", i, fmax, fmin);

			if(log_to_file)
			{
				sprintf(temp_string, "%u,%u,", fmax,fmin);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			snprintf(str1 + len1, SHOWLINESZ -len1, " NA (Err: %-2d)     |", ret);
			snprintf(str2 + len2, SHOWLINESZ - len2, " NA (Err: %-2d)     |", ret);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Fmax(MHz),Fmin(MHz)\n%d,NA (Err:%d),NA (Err:%d)\n", i, ret, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Fmax(MHz)\":\"NA (Err:%d)\",\n\t\t\"Fmin(MHz)\":\"NA (Err:%d)\"\n\t},", i, ret, ret);

			if(log_to_file)
			{
				sprintf(temp_string, "NA (Err:%d),NA (Err:%d),", ret, ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	if(print_results == PRINT_RESULTS)
	{
		printf("%s", str1);
		printf("%s", str2);
	}
}

static int epyc_get_clock_freq(void)
{
	esmi_status_t ret;
	uint32_t i;
	uint32_t fclk, mclk, cclk;
	char str[SHOWLINESZ] = {};
	uint32_t len;
	uint32_t err_bits = 0;
	char *freq_src[ARRAY_SIZE(freqlimitsrcnames) * sys_info.sockets];

	for (i = 0; i < (ARRAY_SIZE(freqlimitsrcnames) * sys_info.sockets); i++)
		freq_src[i] = NULL;

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| Fclk (MHz)\t\t\t |");

	snprintf(str, SHOWLINESZ, "\n| Mclk (MHz)\t\t\t |");
	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d Fclk(MHz),Socket:%d Mclk(MHz),", i, i);
			append_string(&log_file_header, temp_string);
		}
		len = strlen(str);
		ret = esmi_fclk_mclk_get(i, &fclk, &mclk);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17d|", fclk);
			snprintf(str + len, SHOWLINESZ - len, " %-17d|", mclk);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Fclk(MHz),Mclk(MHz)\n%d,%d,%d\n", i, fclk, mclk);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Fclk(MHz)\":%d,\n\t\t\"Mclk(MHz)\":%d\n\t},", i, fclk, mclk);

			if(log_to_file)
			{
				sprintf(temp_string, "%d,%d,", fclk, mclk);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			snprintf(str + len, SHOWLINESZ - len, " NA (Err: %-2d)     |", ret);

			if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Fclk(MHz),Mclk(MHz)\n%d,NA (Err:%d),NA (Err:%d)\n", i, ret, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Fclk(MHz)\":\"NA (Err:%d)\",\n\t\t\"Mclk(MHz)\":\"NA (Err:%d)\"\n\t},", i, ret, ret);

			if(log_to_file)
			{
				sprintf(temp_string, "NA (Err:%d),NA (Err:%d),", ret, ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	if(print_results == PRINT_RESULTS) printf("%s", str);

	if(print_results == PRINT_RESULTS)
		printf("\n| Cclk (MHz)\t\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d CclkLimit(MHz),", i);
			append_string(&log_file_header, temp_string);
		}
		ret = esmi_cclk_limit_get(i, &cclk);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17d|", cclk);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,CclkLimit(MHz)\n%d,%d\n", i, cclk);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"CclkLimit(MHz)\":%d\n\t},", i, cclk);

			if(log_to_file)
			{
				sprintf(temp_string, "%d,", cclk);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,CclkLimit(MHz)\n%d,%d\n", i, cclk);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"CclkLimit(MHz)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file)
			{
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	if (sys_info.show_addon_clock_metrics)
		sys_info.show_addon_clock_metrics(&err_bits, freq_src);

	print_socket_footer();
	if(print_results == PRINT_RESULTS) printf("\n");
	err_bits_print(err_bits);
	if (print_src)
		display_freq_limit_src_names(freq_src);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_apb_enable(uint32_t sock_id)
{
	esmi_status_t ret;

	ret = esmi_apb_enable(sock_id);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to enable DF performance boost algo on "
			"socket[%d], Err[%d]: %s\n", sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	printf("APB is enabled successfully on socket[%d]\n", sock_id);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_df_pstate(uint32_t sock_id, int32_t pstate)
{
	esmi_status_t ret;

	ret = esmi_apb_disable(sock_id, pstate);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] DF pstate\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("APB is disabled, P-state is set to [%d] on socket[%d] successfully\n",
		pstate, sock_id);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_df_pstate(uint32_t sock_id)
{
	esmi_status_t ret;
	uint8_t apb_disabled;
	uint8_t pstate;
	char pstate_string[10];
	char temp_string[300];
	char apb_status_string[20];

	ret = esmi_apb_status_get(sock_id, &apb_disabled, &pstate);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] DF pstate\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d APBStatus,Socket:%d DFPstate,", sock_id, sock_id);
		append_string(&log_file_header, temp_string);
	}
	if(print_results == PRINT_RESULTS)
		printf("-----------------------------------------\n");

	sprintf(apb_status_string, "Enabled");
	sprintf(pstate_string, "-1");
	if(1 == apb_disabled)
	{
		sprintf(apb_status_string, "Disabled");
		sprintf(pstate_string, "%d", pstate);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("| socket[%d] APBStatus\t | %s\t|\n", sock_id, apb_status_string);
		printf("| socket[%d] DFPState\t | %s\t\t|\n", sock_id, pstate_string);
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,APBStatus,DFPstate\n%d,\"%s\",%s\n", sock_id, apb_status_string, pstate_string);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"APBStatus\":\"%s\",\n\t\t\"DFPstate\":%s\n\t},", sock_id, apb_status_string, pstate_string);

	if(log_to_file)
	{
		sprintf(temp_string, "%s,%s,", apb_status_string, pstate_string);
		append_string(&log_file_data, temp_string);
	}

	if(print_results == PRINT_RESULTS)
		printf("-----------------------------------------\n");

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_xgmi_width(uint8_t sock_id, uint8_t min, uint8_t max)
{
	esmi_status_t ret;

	ret = esmi_xgmi_width_set(sock_id, min, max);

	if (ret != ESMI_SUCCESS) {
		printf("Failed to set xGMI link width, socket[%d], Min[%d], Max[%d], Err[%d]: %s\n",
			sock_id, min, max, ret, esmi_get_err_msg(ret));
		if(min > max)
			printf("Note: Please make sure Max >= Min\n");
		return ret;
	}
	printf("Socket[%d] xGMI link width set to Min[%d] and Max[%d] successfully\n", sock_id, min, max);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_xgmi_width(uint8_t sock_id)
{
	esmi_status_t ret;
	uint8_t min;
	uint8_t max;
	char temp_string[300];

	ret = esmi_xgmi_width_get(sock_id, &min, &max);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get xGMI link width for socket [%d], Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d MinXgmiWidth,Socket:%d MaxXgmiWidth,", sock_id, sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("------------------------------------------\n");
		printf("| Socket[%d] MinXgmiWidth\t | %d\t |\n", sock_id, min);
		printf("| Socket[%d] MaxXgmiWidth\t | %d\t |\n", sock_id, max);
		printf("------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,MinXgmiWidth,MaxXgmiWidth\n%d,%d,%d\n", sock_id, min, max);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"MinXgmiWidth\":%d,\n\t\t\"MaxXgmiWidth\":%d\n\t},", sock_id, min, max);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,%d,", min, max);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_lclk_dpm_level(uint8_t sock_id, uint8_t nbio_id, uint8_t min, uint8_t max)
{
	esmi_status_t ret;

	ret = esmi_socket_lclk_dpm_level_set(sock_id, nbio_id, min, max);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set lclk dpm level, socket[%d], nbiod[%d], Min[%d], Max[%d], Err[%d]: %s\n",
			sock_id, nbio_id, min, max, ret, esmi_get_err_msg(ret));
		if(min > max)
			printf("Note: Please make sure Max >= Min\n");
		return ret;
	}
	printf("Socket[%d] nbio[%d] LCLK frequency set to Min[%d] and Max[%d] successfully\n",
		sock_id, nbio_id, min, max);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_lclk_dpm_level(uint32_t sock_id, uint8_t nbio_id)
{
	struct dpm_level nbio;
	esmi_status_t ret;
	char temp_string[300];

	ret = esmi_socket_lclk_dpm_level_get(sock_id, nbio_id, &nbio);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get LCLK dpm level for socket[%d], nbiod[%d], "
		       "Err[%d]: %s\n",
			sock_id, nbio_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d NbioId:%d MinLclkDpmLevel,Socket:%d NbioId:%d MaxLclkDpmLevel,", sock_id, nbio_id, sock_id, nbio_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("\n------------------------------------------------------\n");
		printf("| Socket[%d] NbioId[%d] MinLclkDpmLevel\t | %5u     |\n", sock_id, nbio_id, nbio.min_dpm_level);
		printf("| Socket[%d] NbioId[%d] MaxLclkDpmLevel\t | %5u     |\n", sock_id, nbio_id, nbio.max_dpm_level);
		printf("------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,NbioId,MinLclkDpmLevel,MaxLclkDpmLevel\n%d,%d,%d,%d\n", sock_id, nbio_id, nbio.min_dpm_level, nbio.max_dpm_level);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"NbiodId\":%d,\n\t\t\"MinLclkDpmLevel\":%d,\n\t\t\"MaxLclkDpmLevel\":%d\n\t},", sock_id, nbio_id, nbio.min_dpm_level, nbio.max_dpm_level);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,%d,", nbio.min_dpm_level, nbio.max_dpm_level);
		append_string(&log_file_data, temp_string);
	}
	return ret;
}

static int epyc_get_socketpower(void)
{
	esmi_status_t ret;
	uint32_t i;
	uint32_t power = 0, powerlimit = 0, powermax = 0;
	uint32_t err_bits = 0;
	char temp_string[300];

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| Power (Watts)\t\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d Power(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_power_get(i, &power);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Power(Watts)\n%d,%.3f\n", i, (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Power(Watts)\":%.3f\n\t},", i, (double)power/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)power/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,Power(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Power(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	if(print_results == PRINT_RESULTS)
		printf("\n| PowerLimit (Watts)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d PowerLimit(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_power_cap_get(i, &powerlimit);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)powerlimit/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,PowerLimit(Watts)\n%d,%.3f\n", i, (double)powerlimit/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerLimit(Watts)\":%.3f\n\t},", i, (double)powerlimit/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)powerlimit/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,PowerLimit(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerLimit(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	if(print_results == PRINT_RESULTS)
		printf("\n| PowerLimitMax (Watts)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d PowerLimitMax(Watts),", i);
			append_string(&log_file_header, temp_string);
		}
		ret = esmi_socket_power_cap_max_get(i, &powermax);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)powermax/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,PowerLimitMax(Watts)\n%d,%.3f\n", i, (double)powermax/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerLimitMax(Watts)\":%.3f\n\t},", i, (double)powermax/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)powermax/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,PowerLimitMax(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerLimitMax(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	print_socket_footer();
	if(print_results == PRINT_RESULTS)
		printf("\n");
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_coreperf(uint32_t core_id)
{
	esmi_status_t ret;
	uint32_t boostlimit = 0;
	char temp_string[300];
	/* Get the boostlimit value for a given core */
	ret = esmi_core_boostlimit_get(core_id, &boostlimit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get core[%d] boostlimit, Err[%d]: %s\n",
			core_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Core:%d BoostLimit(MHz),", core_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("--------------------------------------------------\n");
		printf("| core[%03d] boostlimit (MHz)\t | %-10u \t |\n", core_id, boostlimit);
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,BoostLimit(MHz)\n%d,%d\n", core_id, boostlimit);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"BoostLimit(MHz)\":%d\n\t},", core_id, boostlimit);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,", boostlimit);
		append_string(&log_file_data, temp_string);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_floorlimit(uint32_t core_id, enum floorlimit_type type)
{
	esmi_status_t ret;
	uint32_t floorlimit = 0;
	char temp_string[300];
	char floorlimit_type_string[20];

	enum set_get_floorlimit set_get_floorlimit_type = GET_FLOOR_FREQUENCY_CORE;
	strcpy(floorlimit_type_string, "FloorLimit");
	if(type == EFF_FLOOR_FREQUENCY)
	{
		set_get_floorlimit_type = GET_EFF_FLOOR_FREQUENCY_CORE;
		strcpy(floorlimit_type_string, "EffectiveFloorLimit");
	}

	/* Get the floorlimit value for a given core */
	ret = esmi_floorlimit_set_get(core_id, &floorlimit, set_get_floorlimit_type);
	if (ret != ESMI_SUCCESS) {
		printf("Failed, Core:%d, to get %s(MHz), Err[%d]: %s\n", core_id, floorlimit_type_string, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Core:%d %s(MHz),", core_id, floorlimit_type_string);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		if(type == EFF_FLOOR_FREQUENCY) printf("--------");
		printf("--------------------------------------------------\n");
		printf("| core[%03d] %s(MHz)\t | %-10u \t |\n", core_id, floorlimit_type_string, floorlimit);
		if(type == EFF_FLOOR_FREQUENCY) printf("--------");
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,%s(MHz)\n%d,%d\n", floorlimit_type_string, core_id, floorlimit);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"%s(MHz)\":%d\n\t},", core_id, floorlimit_type_string, floorlimit);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,", floorlimit);
		append_string(&log_file_data, temp_string);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_setpowerlimit(uint32_t sock_id, uint32_t power)
{
	esmi_status_t ret;
	uint32_t max_power = 0;
	uint32_t current_power_limit = 0;

	ret = esmi_socket_power_cap_set(sock_id, power);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] powerlimit\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_socket_power_cap_max_get(sock_id, &max_power);
	ret = esmi_socket_power_cap_get(sock_id, &current_power_limit);
	if ((ret == ESMI_SUCCESS) && max_power && (current_power_limit != power) && (power > max_power))
	{
		printf("Input power is more than max power limit,"
			" limiting to %.3f Watts\n",
			(double)current_power_limit/1000);
		power = current_power_limit;
	}
	printf("Socket[%d] power_limit set to %6.03f Watts successfully\n",
		sock_id, (double)power/1000);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_setcoreperf(uint32_t core_id, uint32_t boostlimit)
{
	esmi_status_t ret;
	uint32_t blimit = 0;
	uint16_t fmax = 0;
	uint16_t fmin = 0;

	ret = esmi_core_boostlimit_set(core_id,	boostlimit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set core[%d] boostlimit, Err[%d]: %s\n",
			core_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_core_boostlimit_get(core_id, &blimit);
	if (ret != ESMI_SUCCESS) {
		printf("Core[%d] boostlimit set successfully, but failed to get the value\n",
			core_id);
		return ret;
	}
	ret = esmi_socket_freq_range_get(SOCKET_0, &fmax, &fmin);

	if (blimit < boostlimit) {
		if((fmax) && (blimit != fmax))
		{
			printf("Core[%d] boostlimit set to boost limit: %u MHz\n", core_id, blimit);
		}
		else
		{
			printf("Maximum allowed boost limit is: %u MHz\n", blimit);
			printf("Core[%d] boostlimit set to max boost limit: %u MHz\n", core_id, blimit);
		}
	} else if (blimit > boostlimit) {
		if((fmin) && (blimit != fmin))
		{
			printf("Core[%d] boostlimit set to boost limit: %u MHz\n", core_id, blimit);
		}
		else
		{
			printf("Minimum allowed boost limit is: %u MHz\n", blimit);
			printf("Core[%d] boostlimit set to min boost limit: %u MHz\n", core_id, blimit);
		}
	} else {
		printf("Core[%d] boostlimit set to %u MHz successfully\n", core_id, blimit);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_setsocketperf(uint32_t sock_id, uint32_t boostlimit)
{
	esmi_status_t ret;
	uint32_t blimit = 0, online_core;

	ret = esmi_socket_boostlimit_set(sock_id, boostlimit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] boostlimit\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_first_online_core_on_socket(sock_id, &online_core);
	if (ret != ESMI_SUCCESS) {
		printf("Set Successful, but not verified\n");
		return ret;
	}
	ret = esmi_core_boostlimit_get(online_core, &blimit);
	if (ret != ESMI_SUCCESS) {
		printf("Socket[%d] boostlimit set successfully, but failed to get the value\n",
			sock_id);
		return ret;
	}
	if (blimit < boostlimit)
		printf("Socket[%d] boostlimit set to max boost limit: %u MHz\n", sock_id, blimit);
	else if (blimit > boostlimit)
		printf("Socket[%d] boostlimit set to min boost limit: %u MHz\n", sock_id, blimit);
	else
		printf("Socket[%d] boostlimit set to %u MHz successfully\n", sock_id, blimit);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_floorlimit(uint32_t core_id, uint32_t floorlimit)
{
	esmi_status_t ret;
	uint32_t flimit = floorlimit;
	uint16_t fmax = 0;
	uint16_t fmin = 0;

	ret = esmi_floorlimit_set_get(core_id, &flimit, SET_FLOOR_FREQUENCY_CORE);
	if (ret != ESMI_SUCCESS) {
		printf("Failed, Core:%d, to set to FloorLimit(MHz):%u, Err[%d]: %s\n", core_id, floorlimit, ret, esmi_get_err_msg(ret));
		return ret;
	}

	ret = esmi_floorlimit_set_get(core_id, &flimit, GET_FLOOR_FREQUENCY_CORE);
	ret = esmi_socket_freq_range_get(SOCKET_0, &fmax, &fmin);

	if (flimit < floorlimit) {
		if((fmax) && (flimit != fmax))
		{
			printf("Core:%d, set to FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
		else
		{
			printf("Maximum allowed FloorLimit(MHz):%u\n", flimit);
			printf("Core:%d, set to Max FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
	} else if (flimit > floorlimit) {
		if((fmin) && (flimit != fmin))
		{
			printf("Core:%d, set to FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
		else
		{
			printf("Minimum allowed FloorLimit(MHz):%u\n", flimit);
			printf("Core:%d, set to Min FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
	} else {
		printf("Core:%d, set to FloorLimit(MHz):%u, successfully\n", core_id, flimit);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_socket_floorlimit(uint32_t sock_id, uint32_t floorlimit)
{
	esmi_status_t ret;
	uint32_t flimit = floorlimit;
	uint16_t fmax = 0;
	uint16_t fmin = 0;

	ret = esmi_floorlimit_set_get(sock_id, &flimit, SET_FLOOR_FREQUENCY_SOCKET);
	if (ret != ESMI_SUCCESS) {
		printf("Failed, Socket:%d, to set to FloorLimit(MHz):%u, ",sock_id, floorlimit);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	ret = esmi_floorlimit_set_get(sock_id, &flimit, GET_FLOOR_FREQUENCY_SOCKET);
	ret = esmi_socket_freq_range_get(sock_id, &fmax, &fmin);

	if (flimit < floorlimit) {
		if((fmax) && (flimit != fmax))
		{
			printf("Socket:%d, set to FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
		else
		{
			printf("Maximum allowed FloorLimit(MHz):%u\n", flimit);
			printf("Socket:%d, set to Max FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
	} else if (flimit > floorlimit) {
		if((fmin) && (flimit != fmin))
		{
			printf("Socket:%d, set to FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
		else
		{
			printf("Minimum allowed FloorLimit(MHz):%u\n", flimit);
			printf("Socket:%d, set to Min FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
	} else {
		printf("Socket:%d, set to FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_msr_floorlimit(uint32_t core_id, uint32_t floorlimit)
{
	esmi_status_t ret = ESMI_UNKNOWN_ERROR;
	uint32_t flimit = floorlimit;
	uint16_t fmax = 0;
	uint16_t fmin = 0;

	ret = esmi_socket_freq_range_get(SOCKET_0, &fmax, &fmin);
	if ((ret == ESMI_SUCCESS) && fmax) {
		ret = esmi_msr_floorlimit_set(core_id, flimit, SET_FLOOR_FREQUENCY_CORE, fmax);
	}

	if (ret != ESMI_SUCCESS) {
		printf("Failed, core:%d, to set to Msr FloorLimit(MHz):%u, Err[%d]: %s\n",core_id, flimit, ret, esmi_get_err_msg(ret));
		return ret;
	}
	sleep_in_milliseconds(100);
	ret = esmi_floorlimit_set_get(core_id, &flimit, GET_EFF_FLOOR_FREQUENCY_CORE);
	if(0 == flimit) flimit = floorlimit;
	//if((fmax) && (flimit > fmax)) flimit = fmax;
	//if((fmin) && (flimit < fmin)) flimit = fmin;

	if (flimit < floorlimit) {
		if((fmax) && (flimit != fmax))
		{
			printf("Core:%d, set to Msr FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
		else
		{
			printf("Maximum allowed Msr FloorLimit(MHz):%u\n", flimit);
			printf("Core:%d, set to Max Msr FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
	} else if (flimit > floorlimit) {
		if((fmin) && (flimit != fmin))
		{
			printf("Core:%d, set to Msr FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
		else
		{
			printf("Minimum allowed Msr FloorLimit(MHz):%u\n", flimit);
			printf("Core:%d, set to Min Msr FloorLimit(MHz):%u, successfully\n", core_id, flimit);
		}
	} else {
		printf("Core:%d, set to Msr FloorLimit(MHz):%u, successfully\n", core_id, flimit);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_msr_socket_floorlimit(uint32_t sock_id, uint32_t floorlimit)
{
	esmi_status_t ret = ESMI_UNKNOWN_ERROR;
	uint32_t flimit = floorlimit;
	uint16_t fmax = 0;
	uint16_t fmin = 0;

	ret = esmi_socket_freq_range_get(sock_id, &fmax, &fmin);
	if ((ret == ESMI_SUCCESS) && fmax) {
		ret = esmi_msr_floorlimit_set(sock_id, flimit, SET_FLOOR_FREQUENCY_SOCKET, fmax);
	}
	if (ret != ESMI_SUCCESS) {
		printf("Failed, Socket:%d, to set to Msr FloorLimit(MHz):%u, ",sock_id, flimit);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	sleep_in_milliseconds(100);
	ret = esmi_floorlimit_set_get(sock_id, &flimit, GET_EFF_FLOOR_FREQUENCY_SOCKET);
	if(0 == flimit) flimit = floorlimit;
	//if((fmax) && (flimit > fmax)) flimit = fmax;
	//if((fmin) && (flimit < fmin)) flimit = fmin;

	if (flimit < floorlimit) {
		if((fmax) && (flimit != fmax))
		{
			printf("Socket:%d, set to Msr FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
		else
		{
			printf("Maximum allowed Msr FloorLimit(MHz):%u\n", flimit);
			printf("Socket:%d, set to Max Msr FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
	} else if (flimit > floorlimit) {
		if((fmin) && (flimit != fmin))
		{
			printf("Socket:%d, set to Msr FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
		else
		{
			printf("Minimum allowed Msr FloorLimit(MHz):%u\n", flimit);
			printf("Socket:%d, set to Min Msr FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
		}
	} else {
		printf("Socket:%d, set to Msr FloorLimit(MHz):%u, successfully\n", sock_id, flimit);
	}

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_sockc0_residency(uint32_t sock_id)
{
	esmi_status_t ret;
	uint32_t residency = 0;
	char temp_string[300];

	ret = esmi_socket_c0_residency_get(sock_id, &residency);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] residency, Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d C0Residency(%%),", sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("------------------------------------\n");
		printf("| Socket[%d] C0Residency   | %2u %%   |\n", sock_id, residency);
		printf("------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,C0Residency(%%)\n%d,%d\n", sock_id, residency);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"C0Residency(%%)\":%d\n\t},", sock_id, residency);

	if(log_to_file) {
		sprintf(temp_string, "%d,", residency);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_dimm_temp_range_refresh_rate(uint8_t sock_id, uint8_t dimm_addr)
{
	struct temp_range_refresh_rate rate;
	esmi_status_t ret;
	char temp_string[300];
	char ref_rate_string[20];

	ret = esmi_dimm_temp_range_and_refresh_rate_get(sock_id, dimm_addr, &rate);
	if (ret) {
		printf("Failed to get socket[%u] DIMM temperature range and refresh rate,"
			" Err[%d]: %s\n", sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	switch(rate.ref_rate)
	{
		case 0:sprintf(ref_rate_string, "1x");break;
		case 1:sprintf(ref_rate_string, "2x");break;
		default:sprintf(ref_rate_string, "Reserved");break;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d DimmAddress:0x%x TemperatureRange,Socket:%d DimmAddress:0x%x TemperatureRangeRefreshRate,", sock_id, dimm_addr, sock_id, dimm_addr);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("-----------------------------------------------------------------------");
		printf("\n| Socket[%d] DimmAddress[0x%x] TemperatureRange\t\t |", sock_id, dimm_addr);
		printf(" %-10u |", rate.range);
		printf("\n| Socket[%d] DimmAddress[0x%x] TemperatureRangeRefreshRate|", sock_id, dimm_addr);
		printf(" %-10s |", ref_rate_string);
		printf("\n-----------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,DimmAddress,TemperatureRange,TemperatureRangeRefreshRate\n%d,0x%x,%u,%s\n", sock_id, dimm_addr, rate.range, ref_rate_string);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DimmAddress\":\"0x%x\",\n\t\t\"TemperatureRange\":%d,\n\t\t\"TemperatureRangeRefreshRate\":\"%s\"\n\t},", sock_id, dimm_addr, rate.range, ref_rate_string);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,%s,", rate.range, ref_rate_string);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_dimm_power(uint8_t sock_id, uint8_t dimm_addr)
{
	esmi_status_t ret;
	struct dimm_power d_power;
	char temp_string[300];

	ret = esmi_dimm_power_consumption_get(sock_id, dimm_addr, &d_power);
	if (ret) {
		printf("Failed to get socket[%u] DIMM power and update rate, Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d DimmAddress:0x%x Power(Watts),Socket:%d DimmAddress:0x%x PowerUpdateRate(ms),", sock_id, dimm_addr, sock_id, dimm_addr);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("----------------------------------------------------------------------");
		printf("\n| Socket[%d] DimmAddress[0x%x] Power(Watts)\t\t|", sock_id, d_power.dimm_addr);
		printf(" %-10.3f |", (double)d_power.power/1000);
		printf("\n| Socket[%d] DimmAddress[0x%x] PowerUpdateRate(ms)\t|", sock_id, d_power.dimm_addr);
		printf(" %-10u |", d_power.update_rate);
		printf("\n----------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,DimmAddress,Power(Watts),PowerUpdateRate(ms)\n%d,0x%x,%.3f,%u\n", sock_id, d_power.dimm_addr, (double)d_power.power/1000, d_power.update_rate);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DimmAddress\":\"0x%x\",\n\t\t\"Power(Watts)\":%.3f,\n\t\t\"PowerUpdateRate(ms)\":%d\n\t},", sock_id, d_power.dimm_addr, (double)d_power.power/1000, d_power.update_rate);

	if(log_to_file)
	{
		sprintf(temp_string, "%.3f,%u,", (double)d_power.power/1000, d_power.update_rate);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_dimm_power_data(uint8_t sock_id, union dimm_power_inarg dimm_power_inarg)
{
	esmi_status_t ret;
	struct dimm_power d_power;
	char temp_string[500];
	char dimm_prefix_name[50] = {0};
	if(dimm_power_inarg.info.dimm_flag == MAX_DIMM_POWER)
		sprintf(dimm_prefix_name, "MaxDimmPower");
	else if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
		sprintf(dimm_prefix_name, "TotalDimmPower");

	ret = esmi_dimm_power_consumption_data_get(sock_id, dimm_power_inarg, &d_power);
	if (ret) {
		printf("Failed to get socket[%u] DIMM power and update rate, Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
			sprintf(temp_string, "Socket:%d %s(Watts),Socket:%d %sUpdateRate(ms),", sock_id, dimm_prefix_name, sock_id, dimm_prefix_name);
		else
			sprintf(temp_string, "Socket:%d %sDimmAddress,Socket:%d %s(Watts),Socket:%d %sUpdateRate(ms),", sock_id, dimm_prefix_name, sock_id, dimm_prefix_name, sock_id, dimm_prefix_name);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
		{
			printf("----------------------------------------------------------------------");
			printf("\n| Socket[%d] %s(Watts)\t\t\t|", sock_id, dimm_prefix_name);
			printf(" %-10.3f |", (double)d_power.power*DIMM_POW_MULT_FACTOR/1000);
			printf("\n| Socket[%d] %sUpdateRate(ms)\t\t|", sock_id, dimm_prefix_name);
			printf(" %-10u |", d_power.update_rate);
			printf("\n----------------------------------------------------------------------\n");
		}
		else
		{
			printf("---------------------------------------------------------------------------------");
			printf("\n| Socket[%d] %sDimmAddress[0x%x] %s(Watts)\t   |", sock_id, dimm_prefix_name, d_power.dimm_addr, dimm_prefix_name);
			printf(" %-10.3f |", (double)d_power.power/1000);
			printf("\n| Socket[%d] %sDimmAddress[0x%x] %sUpdateRate(ms)|", sock_id, dimm_prefix_name, d_power.dimm_addr, dimm_prefix_name);
			printf(" %-10u |", d_power.update_rate);
			printf("\n---------------------------------------------------------------------------------\n");
		}
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
	{
		if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
			printf("Socket,%s(Watts),%sUpdateRate(ms)\n%d,%.3f,%u\n", dimm_prefix_name, dimm_prefix_name, sock_id, (double)d_power.power*DIMM_POW_MULT_FACTOR/1000, d_power.update_rate);
		else
			printf("Socket,%sDimmAddress,%s(Watts),%sUpdateRate(ms)\n%d,0x%x,%.3f,%u\n", dimm_prefix_name, dimm_prefix_name, dimm_prefix_name, sock_id, d_power.dimm_addr, (double)d_power.power/1000, d_power.update_rate);
	}
	else if(print_results == PRINT_RESULTS_AS_JSON)
	{
		if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"%s(Watts)\":%.3f,\n\t\t\"%sUpdateRate(ms)\":%d\n\t},", sock_id, dimm_prefix_name, (double)d_power.power*DIMM_POW_MULT_FACTOR/1000, dimm_prefix_name, d_power.update_rate);
		else
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"%sDimmAddress\":\"0x%x\",\n\t\t\"%s(Watts)\":%.3f,\n\t\t\"%sUpdateRate(ms)\":%d\n\t},", sock_id, dimm_prefix_name, d_power.dimm_addr, dimm_prefix_name, (double)d_power.power/1000, dimm_prefix_name, d_power.update_rate);
	}

	if(log_to_file)
	{
		if(dimm_power_inarg.info.dimm_flag == TOTAL_DIMM_POWER)
			sprintf(temp_string, "%.3f,%u,", (double)d_power.power*DIMM_POW_MULT_FACTOR/1000, d_power.update_rate);
		else
			sprintf(temp_string, "0x%x,%.3f,%u,", d_power.dimm_addr, (double)d_power.power/1000, d_power.update_rate);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_dimm_thermal(uint8_t sock_id, uint8_t dimm_addr)
{
	struct dimm_thermal d_sensor;
	esmi_status_t ret;
	char temp_string[300];

	ret = esmi_dimm_thermal_sensor_get(sock_id, dimm_addr, &d_sensor);
	if (ret) {
		printf("Failed to get socket[%u] DIMM temperature and update rate,"
			" Err[%d]: %s\n", sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d DimmAddress:0x%x Temperature('C),Socket:%d DimmAddress:0x%x TemperatureUpdateRate(ms),", sock_id, dimm_addr, sock_id, dimm_addr);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("----------------------------------------------------------------------");
		printf("\n| Socket[%d] DimmAddress[0x%x] Temperature('C)\t\t|", sock_id, d_sensor.dimm_addr);
		printf(" %-10.3f |", d_sensor.temp);
		printf("\n| Socket[%d] DimmAddress[0x%x] TemperatureUpdateRate(ms)\t|", sock_id, d_sensor.dimm_addr);
		printf(" %-10u |", d_sensor.update_rate);
		printf("\n----------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,DimmAddress,Temperature('C),TemperatureUpdateRate(ms)\n%d,0x%x,%.3f,%u\n", sock_id, d_sensor.dimm_addr, d_sensor.temp, d_sensor.update_rate);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DimmAddress\":\"0x%x\",\n\t\t\"Temperature('C)\":%.3f,\n\t\t\"TemperatureUpdateRate(ms)\":%d\n\t},", sock_id, d_sensor.dimm_addr, d_sensor.temp, d_sensor.update_rate);

	if(log_to_file)
	{
		sprintf(temp_string, "%.3f,%u,", d_sensor.temp, d_sensor.update_rate);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_dimm_thermal_data(uint8_t sock_id, union dimm_temp_inarg dimm_temp_inarg)
{
	struct dimm_thermal d_sensor;
	esmi_status_t ret;
	char temp_string[500];
	char dimm_prefix_name[50] = {0};
	if(dimm_temp_inarg.info.dimm_flag == MAX_DIMM_TEMP)
		sprintf(dimm_prefix_name, "MaxDimmTemperature");

	ret = esmi_dimm_thermal_sensor_data_get(sock_id, dimm_temp_inarg, &d_sensor);
	if (ret) {
		printf("Failed to get socket[%u] %sDIMM temperature and update rate,"
			" Err[%d]: %s\n", sock_id, dimm_prefix_name, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d %sDimmAddress,Socket:%d %s('C),Socket:%d %sUpdateRate(ms),", sock_id, dimm_prefix_name, sock_id, dimm_prefix_name, sock_id, dimm_prefix_name);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("----------------------------------------------------------------------------------------------");
		printf("\n| Socket[%d] %sDimmAddress[0x%x] %s('C)\t\t|", sock_id, dimm_prefix_name, d_sensor.dimm_addr, dimm_prefix_name);
		printf(" %-10.3f |", d_sensor.temp);
		printf("\n| Socket[%d] %sDimmAddress[0x%x] %sUpdateRate(ms)|", sock_id, dimm_prefix_name, d_sensor.dimm_addr, dimm_prefix_name);
		printf(" %-10u |", d_sensor.update_rate);
		printf("\n----------------------------------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,%sDimmAddress,%s('C),%sUpdateRate(ms)\n%d,0x%x,%.3f,%u\n", dimm_prefix_name, dimm_prefix_name, dimm_prefix_name, sock_id, d_sensor.dimm_addr, d_sensor.temp, d_sensor.update_rate);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"%sDimmAddress\":\"0x%x\",\n\t\t\"%s('C)\":%.3f,\n\t\t\"%sUpdateRate(ms)\":%d\n\t},", sock_id, dimm_prefix_name, d_sensor.dimm_addr, dimm_prefix_name, d_sensor.temp, dimm_prefix_name, d_sensor.update_rate);

	if(log_to_file)
	{
		sprintf(temp_string, "0x%x,%.3f,%u,", d_sensor.dimm_addr, d_sensor.temp, d_sensor.update_rate);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_curr_freq_limit_core(uint32_t core_id)
{
	esmi_status_t ret;
	uint32_t cclk;

	ret = esmi_current_freq_limit_core_get(core_id, &cclk);
	if (ret) {
		printf("Failed to get current clock frequency limit for core[%3d], Err[%d]: %s\n",
			core_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(print_results == PRINT_RESULTS) {
		printf("-------------------------------------------------");
		printf("\n| Core[%03d] CoreCclkFrequencyLimit(MHz) | %u\t|\n", core_id, cclk);
		printf("-------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,CoreFrequencyLimit(MHz)\n%d,%u\n", core_id, cclk);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"CoreFrequencyLimit(MHz)\":%u\n\t},", core_id, cclk);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Core:%d CoreFrequencyLimit(MHz),", core_id);
			append_string(&log_file_header, temp_string);
		}
		sprintf(temp_string, "%u,", cclk);
		append_string(&log_file_data, temp_string);
	}
	return ret;
}

static int epyc_get_power_telemetry()
{
	esmi_status_t ret;
	uint32_t power;
	int i;
	uint32_t err_bits = 0;

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| SVI Power Telemetry (Watts) \t |");

	for (i = 0; i < sys_info.sockets; i++) {
		char temp_string[300];
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SVIPowerTelemetry(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_pwr_svi_telemetry_all_rails_get(i, &power);
		if(!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SVIPowerTelemetry(Watts)\n%d,%.3f\n", i, (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SVIPowerTelemetry(Watts)\":%.3f\n\t},", i, (double)power/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)power/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SVIPowerTelemetry(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SVIPowerTelemetry(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	print_socket_footer();
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

/* order here should match with bw_string[] in esmi.h */
const char *bw_type_list[3] = {"AGG_BW", "RD_BW", "WR_BW"};

static void find_bwtype_index(char *bw_type, int *bw_type_ind)
{
	int i;

	if (bw_type == NULL)
		return;
	for(i = 0; i < ARRAY_SIZE(bw_type_list); i++) {
		if(!strcmp(bw_type, bw_type_list[i])) {
			*bw_type_ind = i;
			break;
		}
	}

}

static esmi_status_t epyc_get_io_bandwidth_info(uint32_t sock_id, char *link)
{
	esmi_status_t ret;
	uint32_t bw;
	struct link_id_bw_type io_link;
	char temp_string[300];

	io_link.link_name = link;
	/* Aggregate bw = 1 */
	io_link.bw_type = 1 ;
	ret = esmi_current_io_bandwidth_get(sock_id, io_link, &bw);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get io bandwidth width for socket[%u] Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d Link:%s CurrentIOAggregateBandwidth(Mbps),", sock_id, link);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("\n-----------------------------------------------------------\n");
		printf("| Current IO Aggregate bandwidth of link %s | %6u Mbps |\n", link, bw);
		printf("-----------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,Link,CurrentAggregateIOBandwidth(Mbps)\n%d,%s,%u\n", sock_id, link, bw);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Link\":%s,\n\t\t\"CurrentIOAggregateBandwidth(Mbps)\":%u\n\t},", sock_id, link, bw);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", bw);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_get_xgmi_bandwidth_info(uint32_t sock_id, char *link, char *bw_type)
{
	struct link_id_bw_type xgmi_link;
	esmi_status_t ret;
	uint32_t bw;
	int bw_ind = -1;
	char temp_string[300];

	find_bwtype_index(bw_type, &bw_ind);
	if (bw_ind == -1) {
		printf("Please provide valid link bandwidth type.\n");
		printf(MAG "Try --help for more information.\n" RESET);
		return ESMI_INVALID_INPUT;
	}

	xgmi_link.link_name = link;
	xgmi_link.bw_type = 1 << bw_ind;
	ret = esmi_current_xgmi_bw_get(sock_id, xgmi_link, &bw);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get xgmi bandwidth width, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d Link:%s Bw:%s XgmiBandwidth(Mbps),", sock_id, link, bw_type);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("\n-------------------------------------------------------------\n");
		printf("| Current %s bandwidth of xGMI link %s | %6u Mbps |\n", bw_string[bw_ind], link, bw);
		printf("-------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,Link,Bw,XgmiBandwidth(Mbps)\n%d,%s,%s,%u\n", sock_id, link, bw_type, bw);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Link\":\"%s\",\n\t\t\"Bw\":\"%s\",\n\t\t\"XgmiBandwidth(Mbps)\":%u\n\t},", sock_id, link, bw_type, bw);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", bw);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static char *pcie_strings[] = {
	"automatically detect based on bandwidth utilisation",
	"limited to Gen4 rate",
	"limited to Gen5 rate"
};

static esmi_status_t epyc_set_pciegen5_rate_ctl(uint8_t sock_id, uint8_t rate_ctrl)
{
	uint8_t prev_mode;
	esmi_status_t ret;

	ret = esmi_pcie_link_rate_set(sock_id, rate_ctrl, &prev_mode);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set pcie link rate control for socket[%u], Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("Pcie link rate is set to %u (i.e. %s) successfully.\n",
	       rate_ctrl, pcie_strings[rate_ctrl]);
	printf("\nPrevious pcie link rate control was : %u\n", prev_mode);

	return ret;
}

static esmi_status_t epyc_set_power_efficiency_mode(uint32_t sock_id, uint8_t mode, uint32_t *util, uint32_t *ppt_limit)
{
	esmi_status_t ret;
	char power_efficiency_mode_name[50];
	char util_pptlimit[100] = {'\0'};

	if(mode == 0) 	   sprintf(power_efficiency_mode_name, "HighPerformance");
	else if(mode == 1) sprintf(power_efficiency_mode_name, "PowerEfficiency");
	else if(mode == 2) sprintf(power_efficiency_mode_name, "IOPerformance");
	else if(mode == 3) sprintf(power_efficiency_mode_name, "BalancedMemory");
	else if(mode == 4) sprintf(power_efficiency_mode_name, "BalancedCore");
	else if(mode == 5) sprintf(power_efficiency_mode_name, "BalancedCoreMemory");
	else 			   sprintf(power_efficiency_mode_name, "Unknown");

	ret = esmi_pwr_efficiency_mode_set(sock_id, mode, util, ppt_limit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set power efficiency mode for socket[%u], Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5))) {
		sprintf(util_pptlimit, " PwrEfficiencyModeUtil(%%):%d, PwrEfficiencyModePptLimit(Watts):%.3f,", *util, (double)*ppt_limit/1000);
	}

	printf("Socket:%d, PwrEfficiencyModeSelection set to, %d:%s,%s successfully\n", sock_id, mode, power_efficiency_mode_name, util_pptlimit);

	return ret;
}

static esmi_status_t epyc_set_df_pstate_range(uint8_t sock_id, uint8_t min_pstate, uint8_t max_pstate)
{
	esmi_status_t ret;

	ret = esmi_df_pstate_range_set(sock_id, min_pstate, max_pstate);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set df pstate range, socket[%d], Min[%d], Max[%d], Err[%d]: %s\n",
			sock_id, min_pstate, max_pstate, ret, esmi_get_err_msg(ret));
		if(min_pstate < max_pstate)
			printf("Note: Please make sure Min >= Max\n");
		return ret;
	}
	printf("Socket[%d] Data Fabric PState range set to Min[%d] and Max[%d] successfully\n", sock_id, min_pstate, max_pstate);

	return ret;
}

static esmi_status_t epyc_get_df_pstate_range(uint8_t sock_id)
{
	esmi_status_t ret;
	uint8_t max_pstate;uint8_t min_pstate;
	char temp_string[300];

	ret = esmi_df_pstate_range_get(sock_id, &min_pstate, &max_pstate);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set df pstate range, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d MinDfPstate(MHz),Socket:%d MaxDfPstate(MHz),", sock_id, sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS) {
		printf("----------------------------------\n");
		printf("| Socket[%d] MinDFPState\t | %d\t |\n", sock_id, min_pstate);
		printf("| Socket[%d] MaxDFPState\t | %d\t |\n", sock_id, max_pstate);
		printf("----------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,MinDfPstate(MHz),MaxDfPstate(MHz)\n%d,%d,%d\n", sock_id, min_pstate, max_pstate);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"MinDfPstate(MHz)\":%d,\n\t\t\"MaxDfPstate(MHz)\":%d\n\t},", sock_id, min_pstate, max_pstate);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,%d,", max_pstate, min_pstate);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static esmi_status_t epyc_set_gmi3_link_width(uint8_t sock_id, uint8_t min, uint8_t max)
{
	esmi_status_t ret;

	ret = esmi_gmi3_link_width_range_set(sock_id, min, max);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set gmi3 link width for socket[%u] Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	printf("Gmi3 link width range is set successfully\n");

	return ret;
}

static esmi_status_t epyc_get_curr_freq_limit_socket(uint32_t sock_id)
{
	esmi_status_t ret;
	uint32_t cclk;

	ret = esmi_cclk_limit_get(sock_id, &cclk);
	if (ret) {
		printf("Failed to get current clock frequency limit for socket[%d], Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(print_results == PRINT_RESULTS) {
		printf("---------------------------------------------------------");
		printf("\n| Socket[%d] SocketCclkFrequencyLimit(MHz) | %u\t|\n", sock_id, cclk);
		printf("---------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,SocketCclkFrequencyLimit(MHz)\n%d,%u\n", sock_id, cclk);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketCclkFrequencyLimit(MHz)\":%u\n\t},", sock_id, cclk);

	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Socket:%d SocketCclkFrequencyLimit(MHz),", sock_id);
			append_string(&log_file_header, temp_string);
		}
		sprintf(temp_string, "%u,", cclk);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static void show_smi_message(void)
{
	if(print_results == PRINT_RESULTS) 		printf("\n============================= E-SMI ===================================\n\n");
	else if(print_results == PRINT_RESULTS_AS_JSON) printf("{");
}

static void show_smi_end_message(void)
{
	if(print_results == PRINT_RESULTS)		printf("\n============================= End of E-SMI ============================\n");
	else if(print_results == PRINT_RESULTS_AS_JSON)
	{
		printf("\n\t{\n\t\t\"JSONFormatVersion\":1\n\t}");
		printf("\n}\n");
	}
}

static void no_addon_socket_metrics(uint32_t *err_bits, char **freq_src)
{
	return;
}

static void no_addon_cpu_metrics(uint32_t *err_bits)
{
	return;
}

static void no_addon_clock_metrics(uint32_t *err_bits, char **freq_src)
{
	return;
}

static void clock_ver5_metrics(uint32_t *err_bits, char **freq_src)
{
	get_sock_freq_limit(err_bits, freq_src);
	get_sock_freq_range(err_bits);
}

static void socket_ver4_metrics(uint32_t *err_bits, char **freq_src)
{
	esmi_status_t ret;
	uint32_t tmon;
	int i;

	ddr_bw_get(err_bits);
	printf("\n| Temperature ('C)\t\t |");
	for (i = 0; i < sys_info.sockets; i++) {
		ret = esmi_socket_temperature_get(i, &tmon);
		if (!ret) {
			printf(" %-17.3f|", (double)tmon/1000);
		} else {
			*err_bits |= 1 << ret;
			printf(" NA (Err: %-2d)     |", ret);
		}
	}
}

static void socket_ver6_metrics(uint32_t *err_bits, char **freq_src)
{
	get_sock_freq_limit(err_bits, freq_src);
	get_sock_freq_range(err_bits);
}

static void socket_ver5_metrics(uint32_t *err_bits, char **freq_src)
{
	ddr_bw_get(err_bits);
	get_sock_freq_limit(err_bits, freq_src);
	get_sock_freq_range(err_bits);
}

static int show_socket_metrics(uint32_t *err_bits, char **freq_src)
{
	esmi_status_t ret;
	uint32_t i;
	uint64_t pkg_input = 0;
	uint32_t power = 0, powerlimit = 0, powermax = 0;
	uint32_t c0resi;
	char temp_string[300];

	print_socket_header();
	if(print_results == PRINT_RESULTS)
		printf("\n| Energy (K Joules)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketEnergy(KJoules),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_energy_get(i, &pkg_input);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3lf|", (double)pkg_input/1000000000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketEnergy(KJoules)\n%d,%.3f\n", i, (double)pkg_input/1000000000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketEnergy(KJoules)\":%.3f\n\t},", i, (double)pkg_input/1000000000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)pkg_input/1000000000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketEnergy(KJoules)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketEnergy(KJoules)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	if(print_results == PRINT_RESULTS)
		printf("\n| Power (Watts)\t\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketPower(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_power_get(i, &power);
		if (!ret) {

			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPower(Watts)\n%d,%.3f\n", i, (double)power/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPower(Watts)\":%.3f\n\t},", i, (double)power/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)power/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPower(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPower(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}


	if(print_results == PRINT_RESULTS)
		printf("\n| PowerLimit (Watts)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketPowerLimit(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_power_cap_get(i, &powerlimit);
		if (!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)powerlimit/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPowerLimit(Watts)\n%d,%.3f\n", i, (double)powerlimit/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPowerLimit(Watts)\":%.3f\n\t},", i, (double)powerlimit/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)powerlimit/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPowerLimit(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPowerLimit(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	if(print_results == PRINT_RESULTS)
		printf("\n| PowerLimitMax (Watts)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketPowerLimitMax(Watts),", i);
			append_string(&log_file_header, temp_string);
		}

		ret = esmi_socket_power_cap_max_get(i, &powermax);
		if(!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17.3f|", (double)powermax/1000);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPowerLimitMax(Watts)\n%d,%.3f\n", i, (double)powermax/1000);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPowerLimitMax(Watts)\":%.3f\n\t},", i, (double)powermax/1000);

			if(log_to_file) {
				sprintf(temp_string, "%.3f,", (double)powermax/1000);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketPowerLimitMax(Watts)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketPowerLimitMax(Watts)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}

	if(print_results == PRINT_RESULTS)
		printf("\n| C0 Residency (%%)\t\t |");

	for (i = 0; i < sys_info.sockets; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d SocketC0Residency(%%),", i);
			append_string(&log_file_header, temp_string);
		}
		ret = esmi_socket_c0_residency_get(i, &c0resi);
		if(!ret) {
			if(print_results == PRINT_RESULTS)
				printf(" %-17u|", c0resi);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketC0Residency(%%)\n%d,%u\n", i, c0resi);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketC0Residency(%%)\":%u\n\t},", i, c0resi);

			if(log_to_file) {
				sprintf(temp_string, "%u,", c0resi);
				append_string(&log_file_data, temp_string);
			}
		} else {
			*err_bits |= 1 << ret;
			if(print_results == PRINT_RESULTS)
				printf(" NA (Err: %-2d)     |", ret);
			else if(print_results == PRINT_RESULTS_AS_CSV)
				printf("Socket,SocketC0Residency(%%)\n%d,NA (Err:%d)\n", i, ret);
			else if(print_results == PRINT_RESULTS_AS_JSON)
				printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SocketC0Residency(%%)\":\"NA (Err:%d)\"\n\t},", i, ret);

			if(log_to_file) {
				sprintf(temp_string, "NA (Err:%d),", ret);
				append_string(&log_file_data, temp_string);
			}
		}
	}
	/* proto version specific socket metrics are printed here */
	if (sys_info.show_addon_socket_metrics)
		sys_info.show_addon_socket_metrics(err_bits, freq_src);

	print_socket_footer();
	if (*err_bits > 1)
		return ESMI_MULTI_ERROR;
	return ESMI_SUCCESS;
}

static esmi_status_t cache_system_info(void)
{
	esmi_status_t ret;

	ret = esmi_number_of_cpus_get(&sys_info.cpus);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get number of cpus, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_number_of_sockets_get(&sys_info.sockets);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get number of sockets, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_threads_per_core_get(&sys_info.threads_per_core);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get threads per core, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_cpu_family_get(&sys_info.family);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get cpu family, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	ret = esmi_cpu_model_get(&sys_info.model);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get cpu model, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}

	return ESMI_SUCCESS;
}

static void show_system_info(void)
{
	if(print_results == PRINT_RESULTS)
	{
		printf("--------------------------------------\n");
		printf("| CPU Family		| 0x%-2x (%-3d) |\n", sys_info.family, sys_info.family);
		printf("| CPU Model		| 0x%-2x (%-3d) |\n", sys_info.model, sys_info.model);
		printf("| NR_CPUS		| %-8d   |\n", sys_info.cpus);
		printf("| NR_SOCKETS		| %-8d   |\n", sys_info.sockets);
		if (sys_info.threads_per_core > 1) {
			printf("| THREADS PER CORE	| %d (SMT ON) |\n", sys_info.threads_per_core);
		} else {
			printf("| THREADS PER CORE	| %d (SMT OFF)|\n", sys_info.threads_per_core);
		}
		printf("--------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("CpuFamily(Hex),CpuModel(Hex),NumberOfCpus,NumberOfSockets,ThreadsPerCore\n0x%x,0x%x,%d,%d,%d\n", sys_info.family, sys_info.model, sys_info.cpus, sys_info.sockets, sys_info.threads_per_core);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"CpuFamily(Hex)\":\"0x%x\",\n\t\t\"CpuModel(Hex)\":\"0x%x\",\n\t\t\"NumberOfCpus\":%d,\n\t\t\"NumberOfSockets\":%d,\n\t\t\"ThreadsPerCore\":%x\n\t},", sys_info.family, sys_info.model, sys_info.cpus, sys_info.sockets, sys_info.threads_per_core);
}

static esmi_status_t show_cpu_energy_all(void)
{
	int i;
	uint64_t *input;
	uint32_t cpus;
	esmi_status_t ret;
	char temp_string[300];

	cpus = sys_info.cpus/sys_info.threads_per_core;

	input = (uint64_t *) calloc(cpus, sizeof(uint64_t));
	if (NULL == input) {
		printf("Memory allocation failed all energy entries\n");
		return ESMI_NO_MEMORY;
	}

	ret = esmi_all_energies_get(input);
	if (ret != ESMI_SUCCESS) {
		printf("\nFailed: to get CPU Energies, Err[%d]: %s",
			ret, esmi_get_err_msg(ret));
		free(input);
		return ret;
	}
	if(print_results == PRINT_RESULTS)
		printf("\n| CPU Energies in Joules:\t\t\t\t\t\t\t\t\t\t\t|");
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,CoreEnergy(Joules)\n");

	for (i = 0; i < cpus; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Core:%d CoreEnergy(Joules),", i);
			append_string(&log_file_header, temp_string);
		}

		if(print_results == PRINT_RESULTS) {
			if(!(i % 8)) {
				printf("\n| cpu [%3d] :", i);
			}
			printf(" %10.3lf", (double)input[i]/1000000);
			if (i % 8 == 7)
				printf("\t\t|");
		}
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("%d,%.3f\n", i, (double)input[i]/1000000);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"CoreEnergy(Joules)\":%.3f\n\t},", i, (double)input[i]/1000000);

		if(log_to_file) {
			sprintf(temp_string, "%.3f,", (double)input[i]/1000000);
			append_string(&log_file_data, temp_string);
		}
	}
	free(input);

	return ESMI_SUCCESS;
}

static esmi_status_t show_cpu_boostlimit_all(void)
{
	int i;
	uint32_t boostlimit;
	uint32_t cpus;
	esmi_status_t ret;
	char temp_string[300];

	cpus = sys_info.cpus/sys_info.threads_per_core;

	if(print_results == PRINT_RESULTS)
		printf("\n| CPU BoostLimit in MHz:\t\t\t\t\t\t\t\t\t\t\t|");

	for (i = 0; i < cpus; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Core:%d CoreBoostLimit(MHz),", i);
			append_string(&log_file_header, temp_string);
		}

		boostlimit = 0;
		ret = esmi_core_boostlimit_get(i, &boostlimit);
		if(!ret) {
			sprintf(temp_string, "%d", boostlimit);
		} else {
			sprintf(temp_string, "NA");
		}

		if(print_results == PRINT_RESULTS)
		{
			if(!(i % 16)) {
				printf("\n| cpu [%3d] :", i);
			}
			if (!ret) {
				printf(" %-5u", boostlimit);
			} else {
				printf(" NA   ");
			}
			if (i % 16 == 15)
				printf("   |");
		}
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("Core,CoreBoostLimit(MHz)\n%d,%s\n", i, temp_string);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"CoreBoostLimit(MHz)\":%s\n\t},", i, temp_string);

		if(log_to_file) {
			char temp_string_1[300+1];
			sprintf(temp_string_1, "%s,", temp_string);
			append_string(&log_file_data, temp_string_1);
		}
	}

	return ESMI_SUCCESS;
}

static esmi_status_t show_cpu_floorlimit_all(enum floorlimit_type type)
{
	int i;
	uint32_t floorlimit;
	uint32_t cpus;
	esmi_status_t ret;
	char temp_string[300];
	char floorlimit_type_string[20];

	enum set_get_floorlimit set_get_floorlimit_type = GET_FLOOR_FREQUENCY_CORE;
	strcpy(floorlimit_type_string, "FloorLimit");
	if(type == EFF_FLOOR_FREQUENCY)
	{
		set_get_floorlimit_type = GET_EFF_FLOOR_FREQUENCY_CORE;
		strcpy(floorlimit_type_string, "EffectiveFloorLimit");
	}

	cpus = sys_info.cpus/sys_info.threads_per_core;

	if(print_results == PRINT_RESULTS)
		printf("\n| CPU %s in MHz:\t\t\t\t\t\t\t\t\t\t%s|", floorlimit_type_string, (type == EFF_FLOOR_FREQUENCY)?"":"\t");

	for (i = 0; i < cpus; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Core:%d Core%s(MHz),", i, floorlimit_type_string);
			append_string(&log_file_header, temp_string);
		}

		floorlimit = 0;
		ret = esmi_floorlimit_set_get(i, &floorlimit, set_get_floorlimit_type);
		if(!ret) {
			sprintf(temp_string, "%d", floorlimit);
		} else {
			sprintf(temp_string, "NA");
		}

		if(print_results == PRINT_RESULTS)
		{
			if(!(i % 16)) {
				printf("\n| cpu [%3d] :", i);
			}
			if (!ret) {
				printf(" %-5u", floorlimit);
			} else {
				printf(" NA   ");
			}
			if (i % 16 == 15)
				printf("   |");
		}
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("Core,Core%s(MHz)\n%d,%s\n", floorlimit_type_string, i, temp_string);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"Core%s(MHz)\":%s\n\t},", i, floorlimit_type_string, temp_string);

		if(log_to_file) {
			char temp_string_1[300+1];
			sprintf(temp_string_1, "%s,", temp_string);
			append_string(&log_file_data, temp_string_1);
		}
	}

	return ESMI_SUCCESS;
}

static esmi_status_t show_core_clocks_all()
{
	esmi_status_t ret;
	uint32_t cpus;
	uint32_t cclk;
	int i;
	char temp_string[300];

	cpus = sys_info.cpus/sys_info.threads_per_core;

	if(print_results == PRINT_RESULTS)
		printf("\n| CPU core clock current frequency limit in MHz:\t\t\t\t\t\t\t\t|");

	for (i = 0; i < cpus; i++) {
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Core:%d CoreCclkFrequencyLimit(MHz),", i);
			append_string(&log_file_header, temp_string);
		}
	}

	for (i = 0; i < cpus; i++) {
		cclk = 0;
		ret = esmi_current_freq_limit_core_get(i, &cclk);
		if(!ret) {
			sprintf(temp_string, "%d", cclk);
		} else {
			sprintf(temp_string, "NA");
		}
		if(print_results == PRINT_RESULTS) {
			if(!(i % 16)) {
				printf("\n| cpu [%3d] :", i);
			}
			if (!ret) {
				printf(" %-5u", cclk);
			} else {
				printf(" NA   ");
			}
			if (i % 16 == 15)
				printf("   |");
		}
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("Core,CoreCclkFrequencyLimit(MHz)\n%d,%s\n", i, temp_string);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Core\":%d,\n\t\t\"CoreCclkFrequencyLimit(MHz)\":%s\n\t},", i, temp_string);

		if(log_to_file) {
			char temp_string_1[300+1];
			sprintf(temp_string_1, "%s,", temp_string);
			append_string(&log_file_data, temp_string_1);
		}
	}
	return ESMI_SUCCESS;
}

static void cpu_ver5_metrics(uint32_t *err_bits)
{
	esmi_status_t ret;

	if(print_results == PRINT_RESULTS)
		printf("\n--------------------------------------------------------------------"
			"---------------------------------------------");
	ret = show_core_clocks_all();
	*err_bits |= 1 << ret;

	if(print_results == PRINT_RESULTS)
		printf("\n--------------------------------------------------------------------"
			"---------------------------------------------");
}

static int show_cpu_metrics(uint32_t *err_bits)
{
	esmi_status_t ret;

	if(print_results == PRINT_RESULTS)
		printf("\n\n--------------------------------------------------------------------"
			"---------------------------------------------");
	ret = show_cpu_energy_all();
	*err_bits |= 1 << ret;

	if(print_results == PRINT_RESULTS) {
		printf("\n--------------------------------------------------------------------"
			"---------------------------------------------\n");

		printf("\n--------------------------------------------------------------------"
			"---------------------------------------------");
	}
	ret = show_cpu_boostlimit_all();
	*err_bits |= 1 << ret;

	if(print_results == PRINT_RESULTS)
		printf("\n--------------------------------------------------------------------"
			"---------------------------------------------\n");

	/* Floor limit is supported from family 0x1A model 0x50 to 0x5F */
	if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
	{
		if(print_results == PRINT_RESULTS) {
			printf("\n--------------------------------------------------------------------"
				"---------------------------------------------");
		}
		ret = show_cpu_floorlimit_all(FLOOR_FREQUENCY);
		*err_bits |= 1 << ret;

		if(print_results == PRINT_RESULTS)
			printf("\n--------------------------------------------------------------------"
				"---------------------------------------------\n");

				if(print_results == PRINT_RESULTS) {
			printf("\n--------------------------------------------------------------------"
				"---------------------------------------------");
		}
		ret = show_cpu_floorlimit_all(EFF_FLOOR_FREQUENCY);
		*err_bits |= 1 << ret;

		if(print_results == PRINT_RESULTS)
			printf("\n--------------------------------------------------------------------"
				"---------------------------------------------\n");
	}

	/* proto version specific cpu metrics are printed here */
	if (sys_info.show_addon_cpu_metrics)
		sys_info.show_addon_cpu_metrics(err_bits);

	if (*err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

static int test_hsmp_mailbox(uint8_t sock_id, uint32_t input_data)
{
	uint32_t data = input_data;
	esmi_status_t ret;

	ret = esmi_test_hsmp_mailbox(sock_id, &data);
	if (ret) {
		printf("Failed to test hsmp mailbox on socket[%u], Err[%d] : %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if (data != (input_data + 1)) {
		printf("------------------------------------");
		printf("\n| Socket[%u] Test message FAILED | \n", sock_id);
		printf("| Expected : %-5u hex: %#-5x | ", (input_data + 1), (input_data + 1));
		printf("\n| Received : %-5u hex: %#-5x |", data, data);
		printf("\n------------------------------------------\n");
		return 0;
	}
	printf("------------------------------------------");
	printf("\n| Socket[%u] Test message PASSED | \n", sock_id);
	printf("| Expected : %-5u hex: %#-5x | ", (input_data + 1), (input_data + 1));
	printf("\n| Received : %-5u hex: %#-5x |", data, data);
	printf("\n------------------------------------------\n");
	return 0;
}

static int show_smi_all_parameters(void)
{
	char *freq_src[ARRAY_SIZE(freqlimitsrcnames) * sys_info.sockets];
	int i;
	uint32_t err_bits = 0;

	for (i = 0; i < (ARRAY_SIZE(freqlimitsrcnames) * sys_info.sockets); i++)
		freq_src[i] = NULL;

	show_system_info();

	show_socket_metrics(&err_bits, freq_src);

	show_cpu_metrics(&err_bits);

	if(print_results == PRINT_RESULTS)
		printf("\n");
	if (print_src)
		display_freq_limit_src_names(freq_src);
	err_bits_print(err_bits);
	if (err_bits > 1)
		return ESMI_MULTI_ERROR;

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_metrics_table_version(void)
{
	uint32_t met_ver = 0;
	esmi_status_t ret;

	ret = esmi_metrics_table_version_get(&met_ver);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get Metrics Table Version, Err[%d]: %s\n",
			ret, esmi_get_err_msg(ret));
		return ret;
	}
	printf("\n------------------------------------------");
	printf("\n| METRICS TABLE Version   |  0x%x \t |\n", met_ver);
	printf("------------------------------------------\n");

	return ESMI_SUCCESS;
}

uint32_t check_msb_32(uint32_t num)
{
	uint32_t msb;
	msb = 1UL << (NUM_OF_32BITS - 1);
	/*If msb = 1 , then take 2'complement of the number*/
	if(num & msb) {
		num = ~num + 1;
		return num;
	}
	else
		return num;
}

uint64_t check_msb_64(uint64_t num)
{
	uint64_t msb;
	msb = 1UL << (NUM_OF_64BITS - 1);
	/*If msb = 1 , then take 2'complement of the number*/
	if(num & msb) {
		num = ~num + 1;
		return num;
	}
	else
		return num;
}

static esmi_status_t epyc_show_metric_table_F1A_M90_M9F(uint8_t sock_id, bool initialize_smu_data, bool only_display_results)
{
	if(initialize_smu_data)           return ESMI_SUCCESS;
	if(false == only_display_results) return ESMI_SUCCESS;

	esmi_status_t ret;
	struct hsmp_metric_table mtbl = {0};
	time_t rawtime;
	struct tm *timeinfo;
	int i, j, x;
	uint32_t num1 = 0;
	uint64_t num2 = 0;

	double fraction_q10 = 1.0/pow(2,10);
	double fraction_uq10 = fraction_q10;
	double fraction_uq16 = 1.0/pow(2,16) ;

	ret = esmi_metrics_table_get(sock_id, &mtbl);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get Metrics Table for socket [%d], Err[%d]: %s\n",
			sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("-------------------------------------------------------------------------");
	printf("\n| \t\t\tMETRICS TABLE (FAMILY:0x%x,MODEL:0x%x)    \t|", sys_info.family, sys_info.model);
	printf("\n-------------------------------------------------------------------------");
	printf("\n\n-------------------------------------------------------------------------");
	printf("\n| ACCUMULATOR COUNTER                   |  %-20u\t\t|\n", mtbl.accumulation_counter);
	printf("-------------------------------------------------------------------------");
	printf("\n\n");
	printf("-------------------------------------------------------------------------");
	num1 = check_msb_32(mtbl.max_socket_temperature);
	printf("\n| MAX SOCKET TEMP                       |  %18.3lf 'C\t| ",
			(num1 * fraction_q10));
	num1 = check_msb_32(mtbl.max_vr_temperature);
	printf("\n| MAX VR TEMP                           |  %18.3lf 'C\t| ",
			(num1 * fraction_q10));
	num1 = check_msb_32(mtbl.max_hbm_temperature);
	printf("\n| MAX HBM TEMP                          |  %18.3lf 'C\t| ",
			(num1 * fraction_q10));
	num2 = check_msb_64(mtbl.max_socket_temperature_acc);
	printf("\n| MAX SOCKET TEMP ACC                   |  %18.3lf 'C\t| ",
			(num2 * fraction_q10));
	num2 = check_msb_64(mtbl.max_vr_temperature_acc);
	printf("\n| MAX VR TEMP ACC                       |  %18.3lf 'C\t| ",
			(num2 * fraction_q10));
	num2 = check_msb_64(mtbl.max_hbm_temperature_acc);
	printf("\n| MAX HBM TEMP ACC                      |  %18.3lf 'C\t| \n",
			(num2 * fraction_q10));
	printf("-------------------------------------------------------------------------");
	printf("\n");
	printf("\n-----------------------------------------------------------------");
	printf("\n| SOCKET POWER LIMIT                    |  %5.3lf W\t\t| ",
			(mtbl.socket_power_limit * fraction_uq10));
	printf("\n| MAX SOCKET POWER LIMIT                |  %5.3lf W\t\t| ",
			(mtbl.max_socket_power_limit * fraction_uq10));
	printf("\n| SOCKET POWER                          |  %5.3lf W\t\t| \n",
			(mtbl.socket_power * fraction_uq10));

	printf("-----------------------------------------------------------------");
	printf("\n");
	printf("\n-------------------------------------------------------------------------");
	rawtime = (time_t)mtbl.timestamp;
	time (&rawtime);
	timeinfo = localtime (&rawtime);
	printf("\n| TIMESTAMP Raw                         |  %20llu\t\t|", mtbl.timestamp);
	printf("\n| TIMESTAMP Readable                    |  %s\t\t", asctime(timeinfo));

	printf("\n| SOCKET ENERGY ACC                     |  %15.3lf kJ\t\t| ",
			(mtbl.socket_energy_acc * fraction_uq16)/KILO);
	printf("\n| CCD ENERGY ACC                        |  %15.3lf kJ\t\t| ",
			(mtbl.ccd_energy_acc * fraction_uq16)/KILO);
	printf("\n| XCD ENERGY ACC                        |  %15.3lf kJ\t\t| ",
			(mtbl.xcd_energy_acc * fraction_uq16)/KILO);
	printf("\n| AID ENERGY ACC                        |  %15.3lf kJ\t\t| ",
			(mtbl.aid_energy_acc * fraction_uq16)/KILO);
	printf("\n| HBM ENERGY ACC                        |  %15.3lf kJ\t\t| \n",
			(mtbl.hbm_energy_acc * fraction_uq16)/KILO);
	printf("-------------------------------------------------------------------------");
	printf("\n");
	printf("\n-----------------------------------------------------------------");
	printf("\n| CCLK frequency limit                  |  %5.3lf GHz\t\t| ",
			(mtbl.cclk_frequency_limit * fraction_uq10));
	printf("\n| GFXCLK frequency limit                |  %5.3lf MHz\t\t| ",
			(mtbl.gfxclk_frequency_limit * fraction_uq10));
	printf("\n| Effective FCLK frequency              |  %5.3lf MHz\t\t| ",
			(mtbl.fclk_frequency * fraction_uq10));
	printf("\n| Effective UCLK frequency              |  %5.3lf MHz\t\t| \n",
			(mtbl.uclk_frequency * fraction_uq10));

	printf("-----------------------------------------------------------------");
	printf("\n\n-------------------------------------------------------------------------");
	printf("\n| Effective frequency per AID: \t\t\t\t\t\t|");
	printf("\n-------------------------------------------------------------------------");
	printf("\n| AID | SOCCLK \t\t| VCLK \t\t| DCLK \t\t| LCLK \t\t|");
	printf("\n-------------------------------------------------------------------------");
	for(i = 0; i < AID_COUNT ; i++){
		printf("\n| [%d] | %5.3lf MHz\t| %5.3lf MHz\t| %5.3lf MHz\t| %5.3lf MHz\t| ",
				i, (mtbl.socclk_frequency[i] * fraction_uq10),
				(mtbl.vclk_frequency[i] * fraction_uq10),
				(mtbl.dclk_frequency[i] * fraction_uq10),
				(mtbl.lclk_frequency[i] * fraction_uq10));
	}
	printf("\n-------------------------------------------------------------------------\n");
	printf("\n---------------------------------------------------------------------------------------------------------");
	printf("\n| List of supported frequencies(0 means state is not supported):\t\t\t\t\t|");
	printf("\n---------------------------------------------------------------------------------------------------------");
	printf("\n| AID | FCLK \t\t| UCLK \t\t| SOCCLK \t| VCLK \t\t| DCLK \t\t| LCLK \t\t|");
	printf("\n---------------------------------------------------------------------------------------------------------");
	for(i = 0; i < AID_COUNT ; i++){
		printf("\n| [%d] |%5.3lf MHz\t|%5.3lf MHz\t|%5.3lf MHz\t|%5.3lf MHz\t|%5.3lf MHz\t|%5.3lf MHz\t|",
				i, (mtbl.fclk_frequency_table[i] * fraction_uq10),
				(mtbl.uclk_frequency_table[i] * fraction_uq10),
				(mtbl.socclk_frequency_table[i] * fraction_uq10),
				(mtbl.vclk_frequency_table[i] * fraction_uq10),
				(mtbl.dclk_frequency_table[i] * fraction_uq10),
				(mtbl.lclk_frequency_table[i] * fraction_uq10));
	}
	printf("\n--------------------------------------------------------------------------------------------------------\n");

	uint32_t cpus = sys_info.cpus/sys_info.threads_per_core;
	printf("\n---------------------------------------------------------------------------------------------------");
	printf("---------------");
	printf("\n| CCLK frequency accumulated for target CPUs:\t\t\t\t\t\t\t\t\t |");
	printf("\n--------------------------------------------------------------------------------------------------");
	printf("---------------");
	printf("\n");
	x = 0;
	for(i = 0; i < (cpus/COLS); i++){
	x = i;
	for (j = 0; j < (cpus/COLS); j++) {
	    if(x < 10)
		    printf("| CPU[0%d] :%21.3lf GHz",x, (mtbl.cclk_frequency_acc[x] * fraction_uq10));
	    else
		    printf("| CPU[%d] :%21.3lf GHz",x, (mtbl.cclk_frequency_acc[x] * fraction_uq10));

	    x = x + (cpus/COLS);
	    if (x > (cpus-1))
		    break;
	    printf("  ");
	}
	printf(" |\n");
	}
	printf("--------------------------------------------------------------------------------------------------");
	printf("----------------\n");

	printf("\n---------------------------------------------------------");
	printf("\n| Frequency per target XCC:\t\t\t\t|");
	printf("\n---------------------------------------------------------");
	printf("\n| XCC | GFXCLK ACC\t\t\t| GFXCLK \t|");
	printf("\n---------------------------------------------------------");
	for(i = 0; i < XCC_COUNT; i++){
		printf("\n| [%d] |  %20.3lf MHz\t| %5.3lf MHz\t| ",
				i, (mtbl.gfxclk_frequency_acc[i] * fraction_uq10),
				(mtbl.gfxclk_frequency[i] * fraction_uq10));
	}
	printf("\n---------------------------------------------------------");
	printf("\n");

	printf("\n-----------------------------------------------------------------");
	printf("\n| Max CCLK frequency supported by CPU   |  %5.3lf GHz\t\t| ",
			(mtbl.max_cclk_frequency * fraction_uq10));
	printf("\n| Min CCLK frequency supported by CPU   |  %5.3lf GHz\t\t| ",
			(mtbl.min_cclk_frequency * fraction_uq10));
	printf("\n| Max GFXCLK supported by accelerator   |  %5.3lf MHz\t\t| ",
			(mtbl.max_gfxclk_frequency * fraction_uq10));
	printf("\n| Min GFXCLK supported by accelerator   |  %5.3lf MHz\t\t| ",
			(mtbl.min_gfxclk_frequency * fraction_uq10));
	printf("\n| Max LCLK DPM state range              |  %u \t\t\t| ",
			mtbl.max_lclk_dpm_range);
	printf("\n| Min LCLK DPM state range              |  %u \t\t\t| ",
			mtbl.min_lclk_dpm_range);
	printf("\n------------------------------------------------------------------");

	printf("\n");
	printf("\n-----------------------------------------------------------------");
	printf("\n| Current operating XGMI link width     |  %5.3lf \t\t |",
			(mtbl.xgmi_width * fraction_uq10));
	printf("\n| Current operating XGMI link bitrate   |  %5.3lf Gbps\t\t |",
			(mtbl.xgmi_bitrate * fraction_uq10));
	printf("\n------------------------------------------------------------------");

	printf("\n");
	printf("\n---------------------------------------------------------------------------");
	printf("\n| XGMI Bandwidth accumulated per XGMI link in local socket\t\t  |");
	printf("\n---------------------------------------------------------------------------");
	printf("\n| Link  | \tXGMI Read BW\t\t| \tXGMI Write BW\t\t  |");
	printf("\n---------------------------------------------------------------------------");
	for(i = 0; i < NUM_XGMI_LINKS; i++){
		printf("\n| [%d] \t|  %18.3lf Gbps\t| %18.3lf Gbps\t  |",
				i, (mtbl.xgmi_read_bandwidth_acc[i] * fraction_uq10),
				(mtbl.xgmi_write_bandwidth_acc[i] * fraction_uq10));
	}
	printf("\n--------------------------------------------------------------------------");
	printf("\n");

	printf("\n--------------------------------------------------------------------------");
	printf("\n| Avg C0 residency of all enabled cores |  %18.3lf %% \t |",
            (mtbl.socket_c0_residency * fraction_uq10));
	printf("\n| Avg XCC busy for all enabled XCCs     |  %18.3lf %% \t |",
            (mtbl.socket_gfx_busy * fraction_uq10));
	printf("\n| HBM BW utilization for all HBM stacks |  %18.3lf %% \t |",
            (mtbl.dram_bandwidth_utilization * fraction_uq10));
	printf("\n| Acc value of SocketC0Residency        |  %18.3lf \t\t |",
            (mtbl.socket_c0_residency_acc * fraction_uq10));
	printf("\n| Acc value of SocketGfxBusy            |  %18.3lf \t\t |",
            (mtbl.socket_gfx_busy_acc * fraction_uq10));
	printf("\n| HBM BW for all socket HBM stacks      |  %18.3lf Gbps \t |",
            (mtbl.dram_bandwidth_acc * fraction_uq10));
	printf("\n| Max HBM BW running at max UCLK freq   |  %18.3lf Gbps \t |",
            (mtbl.max_dram_bandwidth * fraction_uq10));
	printf("\n| Acc value of Dram BW Utilization      |  %18.3lf \t\t |",
            (mtbl.dram_bandwidth_utilization_acc * fraction_uq10));
	printf("\n--------------------------------------------------------------------------");

	printf("\n");
	printf("\n--------------------------------------------------------------------------------");
	for(i = 0; i < AID_COUNT; i++)
		printf("\n| PCIe BW for devs connected to AID[%d]  |  %18.3lf Gbps\t\t| ", i,
			(mtbl.pcie_bandwidth_acc[i] * fraction_uq10));
	printf("\n--------------------------------------------------------------------------------");
	printf("\n");
	printf("\n---------------------------------------------------------");
	printf("\n| Active controllers		        | Acc value\t|");
	printf("\n---------------------------------------------------------");
	printf("\n| Prochot                               |  %-10u \t|", mtbl.prochot_residency_acc);
	printf("\n| PPT controller                        |  %-10u \t|", mtbl.ppt_residency_acc);
	printf("\n| Socket thermal throttling controller  |  %-10u \t|",
			mtbl.socket_thm_residency_acc);
	printf("\n| VR thermal throttling controller      |  %-10u \t| ",
			mtbl.vr_thm_residency_acc);
	printf("\n| HBM thermal throttling controller     |  %-10u \t| ",
			mtbl.hbm_thm_residency_acc);
	printf("\n---------------------------------------------------------\n");
	printf("\n");

	return ESMI_SUCCESS;
}
#define MAX_64BITS 0xFFFFFFFFFFFFFFFF
uint64_t calculate_acc_counter_64bit(uint64_t curr, uint64_t prev)
{
	uint64_t acc_counter = 0;
	if(curr >= prev)
	{
		acc_counter = curr - prev;
	}
	else
	{
		acc_counter = (MAX_64BITS - prev) + curr + 1;
	}

	return acc_counter;
}

#define MAX_32BITS 0xFFFFFFFF
uint32_t calculate_acc_counter_32bit(uint32_t curr, uint32_t prev)
{
	uint32_t acc_counter = 0;
	if(curr >= prev)
	{
		acc_counter = curr - prev;
	}
	else
	{
		acc_counter = (MAX_32BITS - prev) + curr + 1;
	}
	return acc_counter;
}

static esmi_status_t epyc_show_metric_table_F1A_M50_M5F_ver_0x00700000(uint8_t sock_id, uint32_t ver, uint64_t sleep_time_in_milliseconds, bool initialize_smu_data, bool only_display_results)
{
	esmi_status_t ret;
	time_t rawtime;
	struct tm *timeinfo;

	uint32_t max_cores_per_ccd = 0;
	if (ver == 0x00700000) {
		max_cores_per_ccd = F1A_M50_M5F_MAX_CORES_PER_CCD_32;
	}

	double fraction_q10 = 1/pow(2,10);// 1/1024
	double fraction_uq10 = fraction_q10;
	double fraction_uq16 = 1/pow(2,16) ;// 1/65536

	static bool first_time_read_socket[F1A_M50_M5F_MAX_SOCKET] = {true, true};
	static struct hsmp_metric_table_f1a_m50_5f mtbl_prev_socket[F1A_M50_M5F_MAX_SOCKET];
	static struct hsmp_metric_table_f1a_m50_5f mtbl_current_socket[F1A_M50_M5F_MAX_SOCKET];

	if(false == only_display_results)
	{
		if (ver == 0x00700000) {
			uint64_t hsmp_table_size = 0;
			hsmp_table_size = sizeof(struct hsmp_metric_table_f1a_m50_5f);

			if (first_time_read_socket[sock_id]) {
				ret = esmi_metrics_table_F1A_M50_M5F_get(sock_id, &mtbl_prev_socket[sock_id], hsmp_table_size);
				if (ret != ESMI_SUCCESS) {
					printf("Socket:%d, Failed to get Metrics Table, Err[%d]: %s\n",
						sock_id, ret, esmi_get_err_msg(ret));
					return ret;
				}
				first_time_read_socket[sock_id] = false;
				if(initialize_smu_data) return ESMI_SUCCESS;
			}
			static bool check_stopmetric = false;
			if(stopmetric_file_name && (false == check_stopmetric)/* && loop_count*/) {
				check_stopmetric = true;

				int temp_loop_counter = 0;
				while(1)
				{
					sleep_in_milliseconds(100);

					FILE *temp_file_fp =  NULL;
					temp_file_fp = fopen(stopmetric_file_name, "r");
					if(temp_file_fp != NULL) {
						if(print_results == PRINT_RESULTS) printf("StopMetric file \"%s\" is present, ...\n", stopmetric_file_name);
						fclose(temp_file_fp);
						temp_file_fp = NULL;
						break;
					}
					else
					{
						if((print_results == PRINT_RESULTS) && (0 == temp_loop_counter))
								printf("StopMetric file \"%s\" is not present, waiting for file \"%s\" to be created to Stop Metric Collection, ...\n", stopmetric_file_name, stopmetric_file_name);
					}
					temp_loop_counter++;
				}
				if(print_results == PRINT_RESULTS) printf("Stopped Metric Collection, ...\n\n");
			}
			if((print_results == PRINT_RESULTS) /*&& (NULL == log_file_name)*/ /*&& ((0 != sleep_time_in_milliseconds) || (NULL != stopmetric_file_name))*/)
					printf("Socket:%d, Gathering MetricTable Data, ...\n", sock_id);
			ret = esmi_metrics_table_F1A_M50_M5F_get(sock_id, &mtbl_current_socket[sock_id], hsmp_table_size);
			if (ret != ESMI_SUCCESS) {
				printf("Socket:%d, Failed to get Metrics Table, Err[%d]: %s\n",
						sock_id, ret, esmi_get_err_msg(ret));
				return ret;
			}
		}
		else
		{
			printf("Socket:%d, Failed to get Metric Table, MetricTableVersion:0x%x not Supported\n", sock_id, ver);
			return ESMI_METRICS_TABLE_NOT_SUPPORTED;
		}
		return ESMI_SUCCESS;
	}

	uint32_t acc_counter_delta = calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.accumulation_counter, mtbl_prev_socket[sock_id].iod.accumulation_counter);
	int maxCCDsAvailable = (mtbl_current_socket[sock_id].iod.num_active_ccds > F1A_M50_M5F_MAX_CCD) ? F1A_M50_M5F_MAX_CCD : mtbl_current_socket[sock_id].iod.num_active_ccds;
	if(print_results == PRINT_RESULTS)
	{
		rawtime = (time_t)mtbl_current_socket[sock_id].iod.timestamp;
		time(&rawtime);
		timeinfo = localtime(&rawtime);
		char date_buffer[100], time_buffer[100];
		strftime(date_buffer, sizeof(date_buffer), "\"%Y-%m-%d\"", timeinfo);
		strftime(time_buffer, sizeof(time_buffer), "\"%H:%M:%S\"", timeinfo);

		printf("\nSOCKET:%d", sock_id);
		printf("\n");
		printf("%-20s\n", "  GENERIC");
		printf("%-20s 0x%-15x\n", "    MetricTableVersion", ver);
		printf("%-20s   0x%-15X\n", "    Family", sys_info.family);
		printf("%-20s   0x%-15x\n", "    Model", sys_info.model);
		printf("%-20s   %-15llu\n", "    TimestampRaw", mtbl_current_socket[sock_id].iod.timestamp);
		printf("%-20s   %-15s\n", "    TimestampDate", date_buffer);
		printf("%-20s   %-15s\n", "    TimestampTime", time_buffer);
		printf("%-20s   %-15u\n", "    NumOfActiveCCDs", mtbl_current_socket[sock_id].iod.num_active_ccds);
		if (metric_table_format) {
			printf("%-20s   %-15u\n", "    AccumCounter", acc_counter_delta);
		}
		else {
			printf("%-20s   %-15u\n", "    AccumCounter", mtbl_current_socket[sock_id].iod.accumulation_counter);
		}

		/* Print summary values directly without variables */
		printf("\n");
		if (metric_table_format) {
			printf("%-20s\n", "  TEMPERATURE");
			printf("%-20s   %-15.2f\n", "    MaxTemp('C)",
				calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.max_socket_temperature_acc, mtbl_prev_socket[sock_id].iod.max_socket_temperature_acc) / acc_counter_delta * fraction_q10);
			printf("\n");

			printf("%-20s\n", "  POWER");
			printf("%-20s   %-15.2f\n", "    PowerLimit(W)",
				mtbl_current_socket[sock_id].iod.socket_power_limit * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    MaxPowerLimit(W)",
				mtbl_current_socket[sock_id].iod.max_socket_power_limit * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    Power(W)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_power_acc, mtbl_prev_socket[sock_id].iod.socket_power_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    CorePower(W)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.core_power_acc, mtbl_prev_socket[sock_id].iod.core_power_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    UncorePower(W)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.uncore_power_acc, mtbl_prev_socket[sock_id].iod.uncore_power_acc) / acc_counter_delta * fraction_uq10);
			printf("\n");

			// ENERGY summary after POWER
			printf("%-20s\n", "  ENERGY");
			printf("%-20s   %-15.2f\n", "    SocketEnergy(KJ)",
				(mtbl_current_socket[sock_id].iod.socket_energy_acc * fraction_uq16) / KILO);
			printf("%-20s   %-15.2f\n", "    CoreEnergy(KJ)",
				(mtbl_current_socket[sock_id].iod.core_energy_acc * fraction_uq16) / KILO);
			printf("%-20s   %-15.2f\n", "    UncoreEnergy(KJ)",
				(mtbl_current_socket[sock_id].iod.uncore_energy_acc * fraction_uq16) / KILO);
			printf("\n");

			printf("%-20s\n", "  FREQUENCY");
			printf("%-20s   %-15.2f\n", "    FCLK(MHz)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.fclk_frequency_acc,
					mtbl_prev_socket[sock_id].iod.fclk_frequency_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    UCLK(MHz)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.uclk_frequency_acc,
					mtbl_prev_socket[sock_id].iod.uclk_frequency_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    DDRRate(MHz)",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.ddr_rate_acc,
					mtbl_prev_socket[sock_id].iod.ddr_rate_acc) / acc_counter_delta * fraction_uq10);
			for (int i = 0; i < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; i++) {
				char lclk_label[32];
				snprintf(lclk_label, sizeof(lclk_label), "    LCLK%d(MHz)", i);
				printf("%-20s   %-15.2f\n", lclk_label,
					calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.lclk_frequency_acc[i],
						mtbl_prev_socket[sock_id].iod.lclk_frequency_acc[i]) / acc_counter_delta * fraction_uq10);
			}
			printf("\n");
		} else {
			printf("%-20s\n", "  TEMPERATURE");
			printf("%-20s   %llu\n", "    MaxTemp",
				(unsigned long long)mtbl_current_socket[sock_id].iod.max_socket_temperature_acc);
			printf("\n");

			printf("%-20s\n", "  POWER");
			printf("%-20s   %u\n", "    PowerLimit",
				mtbl_current_socket[sock_id].iod.socket_power_limit);
			printf("%-20s   %u\n", "    MaxPowerLimit",
				mtbl_current_socket[sock_id].iod.max_socket_power_limit);
			printf("%-20s   %llu\n", "    Power",
				(unsigned long long)mtbl_current_socket[sock_id].iod.socket_power_acc);
			printf("%-20s   %llu\n", "    CorePower",
				(unsigned long long)mtbl_current_socket[sock_id].iod.core_power_acc);
			printf("%-20s   %llu\n", "    UncorePower",
				(unsigned long long)mtbl_current_socket[sock_id].iod.uncore_power_acc);
			printf("\n");

			// ENERGY summary after POWER
			printf("%-20s\n", "  ENERGY");
			printf("%-20s   %llu\n", "    SocketEnergy",
				(unsigned long long)mtbl_current_socket[sock_id].iod.socket_energy_acc);
			printf("%-20s   %llu\n", "    CoreEnergy",
				(unsigned long long)mtbl_current_socket[sock_id].iod.core_energy_acc);
			printf("%-20s   %llu\n", "    UncoreEnergy",
				(unsigned long long)mtbl_current_socket[sock_id].iod.uncore_energy_acc);
			printf("\n");

			printf("%-20s\n", "  FREQUENCY");
			printf("%-20s   %llu\n", "    FCLK",
				(unsigned long long)mtbl_current_socket[sock_id].iod.fclk_frequency_acc);
			printf("%-20s   %llu\n", "    UCLK",
				(unsigned long long)mtbl_current_socket[sock_id].iod.uclk_frequency_acc);
			printf("%-20s   %llu\n", "    DDRRate",
				(unsigned long long)mtbl_current_socket[sock_id].iod.ddr_rate_acc);
			for (int i = 0; i < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; i++) {
				char lclk_label[32];
				snprintf(lclk_label, sizeof(lclk_label), "    LCLK%d", i);
				printf("%-20s   %llu\n", lclk_label,
					(unsigned long long)mtbl_current_socket[sock_id].iod.lclk_frequency_acc[i]);
			}
			printf("\n");
		}

		/* Frequency Table */
		printf("%-20s\n", "  FREQUENCY RANGE: FREQUENCY TABLE");
		if (metric_table_format) {
			/* Human readable format */
			printf("    %-18s %-18s %-18s %-18s %-18s\n", "PSTATE#", "FCLK(MHz)", "UCLK(MHz)", "DDRRate(MHz)", "LCLK(MHz)");
			for (int i = 0; i < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; i++) {
				printf("    PSTATE%-12d %-18.3lf %-18.3lf %-18.3lf %-18.3lf\n",
				i,
				mtbl_current_socket[sock_id].iod.fclk_frequency_table[i] * fraction_uq10,
				mtbl_current_socket[sock_id].iod.uclk_frequency_table[i] * fraction_uq10,
				mtbl_current_socket[sock_id].iod.ddr_rate_table[i] * fraction_uq10,
				mtbl_current_socket[sock_id].iod.lclk_frequency_table[i] * fraction_uq10);
			}
		} else {
			/* Raw metric format - 64-bit values */
			printf("    %-18s %-20s %-20s %-20s %-20s\n", "PSTATE#", "FCLK", "UCLK", "DDRRate", "LCLK");
			for (int i = 0; i < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; i++) {
				printf("    PSTATE%-12d %-20llu %-20llu %-20llu %-20llu\n",
				i,
				(unsigned long long)mtbl_current_socket[sock_id].iod.fclk_frequency_table[i],
				(unsigned long long)mtbl_current_socket[sock_id].iod.uclk_frequency_table[i],
				(unsigned long long)mtbl_current_socket[sock_id].iod.ddr_rate_table[i],
				(unsigned long long)mtbl_current_socket[sock_id].iod.lclk_frequency_table[i]);
			}
		}
		printf("\n");

		/* DfPstate and LclkDpm Ranges - Table Format */
		printf("%-20s\n", "  FREQUENCY RANGE: DFPSTATE and LCLK");
		printf("    %-18s %u\n", "MaxDfPstate", mtbl_current_socket[sock_id].iod.max_df_pstate_range);
		printf("    %-18s %u\n", "MinDfPstate", mtbl_current_socket[sock_id].iod.min_df_pstate_range);
		printf("    %-18s %u\n", "MaxLclkDpm", mtbl_current_socket[sock_id].iod.max_lclk_dpm_range);
		printf("    %-18s %u\n", "MinLclkDpm", mtbl_current_socket[sock_id].iod.min_lclk_dpm_range);
		printf("\n");

		/* XGMI Table */
		printf("%-20s\n", "  XGMI");
		if (metric_table_format) {
			/* Human readable format */
			printf("    %-18s %-18s %-18s %-18s\n", "XGMI#", "BIT_RATE(GB/s)", "READ_BW(GB/s)", "WRITE_BW(GB/s)");
			for (int i = 0; i < F1A_M50_M5F_MAX_XGMI; i++) {
				double xgmi_bit_rate = (double)calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_bit_rate[i],
				mtbl_prev_socket[sock_id].iod.xgmi_bit_rate[i]) / acc_counter_delta * fraction_uq10;
				double xgmi_rd_bw = (double)calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_read_bandwidth[i],
				mtbl_prev_socket[sock_id].iod.xgmi_read_bandwidth[i]) / acc_counter_delta * fraction_uq10;
				double xgmi_wr_bw = (double)calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_write_bandwidth[i],
				mtbl_prev_socket[sock_id].iod.xgmi_write_bandwidth[i]) / acc_counter_delta * fraction_uq10;
				printf("    XGMI%-14d %-18.3lf %-18.3lf %-18.3lf\n", i, xgmi_bit_rate, xgmi_rd_bw, xgmi_wr_bw);
			}
		} else {
			/* Raw metric format - 64-bit values */
			printf("    %-18s %-20s %-20s %-20s\n", "XGMI#", "BIT_RATE", "READ_BW", "WRITE_BW");
			for (int i = 0; i < F1A_M50_M5F_MAX_XGMI; i++) {
				printf("    XGMI%-14d %-20llu %-20llu %-20llu\n", i,
				(unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_bit_rate[i],
				(unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_read_bandwidth[i],
				(unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_write_bandwidth[i]);
			}
		}
		printf("\n");

		uint32_t total_cores_enabled = 1;
		esmi_number_of_physicalcores_in_socket_get(sock_id, &total_cores_enabled);
		/* Residency Summary */
		printf("%-20s\n", "  ACTIVITY: RESIDENCY");
		if (metric_table_format) {
			printf("%-20s   %-15.2f\n", "    AvgCoreC0(%)",
				(calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_c0_residency_acc,
					mtbl_prev_socket[sock_id].iod.socket_c0_residency_acc) / acc_counter_delta * fraction_uq10 * 100) / total_cores_enabled);
#if 0
			printf("%-20s   %-15.2f\n", "    C0AllCores",
				(calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_c0_residency_acc,
					mtbl_prev_socket[sock_id].iod.socket_c0_residency_acc) / acc_counter_delta * fraction_uq10 * 100));
#endif
			printf("%-20s   %-15.2f\n", "    AvgDFCstate(%)",
				(calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_df_cstate_residency_acc,
					mtbl_prev_socket[sock_id].iod.socket_df_cstate_residency_acc) / acc_counter_delta * fraction_uq10));
		} else {
			printf("%-20s   %-15llu\n", "    C0",
				(unsigned long long)mtbl_current_socket[sock_id].iod.socket_c0_residency_acc);
			printf("%-20s   %-15llu\n", "    DFCstate",
				(unsigned long long)mtbl_current_socket[sock_id].iod.socket_df_cstate_residency_acc);
		}
		printf("\n");

		/* Memory Bandwidth Summary */
		if (metric_table_format) {
			printf("%-20s\n", "  ACTIVITY: MEMORY BANDWIDTH");
			printf("%-20s   %-15.2f\n", "    DRAMReadBW",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.dram_read_bandwidth_acc,
					mtbl_prev_socket[sock_id].iod.dram_read_bandwidth_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    DRAMWriteBW",
				calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.dram_write_bandwidth_acc,
					mtbl_prev_socket[sock_id].iod.dram_write_bandwidth_acc) / acc_counter_delta * fraction_uq10);
			printf("%-20s   %-15.2f\n", "    DRAMMaxBW",
					mtbl_current_socket[sock_id].iod.max_dram_bandwidth * fraction_uq10);
		} else {
			printf("%-20s\n", "  ACTIVITY: MEMORY BANDWIDTH");
			printf("%-20s   %-20llu\n", "    DRAMReadBW",
				(unsigned long long)mtbl_current_socket[sock_id].iod.dram_read_bandwidth_acc);
			printf("%-20s   %-20llu\n", "    DRAMWriteBW",
				(unsigned long long)mtbl_current_socket[sock_id].iod.dram_write_bandwidth_acc);
			printf("%-20s   %-20llu\n", "    DRAMMaxBW",
				(unsigned long long)mtbl_current_socket[sock_id].iod.max_dram_bandwidth);
		}
		printf("\n");

		/* PCIe Bandwidth Table */
		printf("%-20s\n", "  ACTIVITY: PCIE BANDWIDTH");
		if (metric_table_format) {
			printf("    %-18s %-18s\n", "PCIE#", "BANDWIDTH(GB/s)");
			for (int i = 0; i < F1A_M50_M5F_MAX_PCIE; i++) {
				double pcie_bw = (double)calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.pcie_bandwidth_acc[i],
				mtbl_prev_socket[sock_id].iod.pcie_bandwidth_acc[i]) / acc_counter_delta * fraction_uq10;
				printf("    PCIE%-14d %-18.3lf\n", i, pcie_bw);
			}
		} else {
			printf("    %-18s %-18s\n", "PCIE#", "BANDWIDTH");
			for (int i = 0; i < F1A_M50_M5F_MAX_PCIE; i++) {
				printf("    PCIE%-14d %-20llu\n", i, (unsigned long long)mtbl_current_socket[sock_id].iod.pcie_bandwidth_acc[i]);
			}
		}
		printf("\n");

		/* Residency Counters Table */
		printf("%-20s\n", "  THROTTLERS");
		if (metric_table_format) {
			struct { const char *name; uint32_t curr; uint32_t prev; } residencies[] = {
				{"Prochot", mtbl_current_socket[sock_id].iod.prochot_residency_acc, mtbl_prev_socket[sock_id].iod.prochot_residency_acc},
				{"Ppt", mtbl_current_socket[sock_id].iod.ppt_residency_acc, mtbl_prev_socket[sock_id].iod.ppt_residency_acc},
				{"Thm", mtbl_current_socket[sock_id].iod.thm_residency_acc, mtbl_prev_socket[sock_id].iod.thm_residency_acc},
				{"VrHot", mtbl_current_socket[sock_id].iod.vrhot_residency_acc, mtbl_prev_socket[sock_id].iod.vrhot_residency_acc},
				{"CpuTdc", mtbl_current_socket[sock_id].iod.cpu_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.cpu_tdc_residency_acc},
				{"SocTdc", mtbl_current_socket[sock_id].iod.soc_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.soc_tdc_residency_acc},
				{"IoMemTdc", mtbl_current_socket[sock_id].iod.io_mem_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.io_mem_tdc_residency_acc},
				{"Fit", mtbl_current_socket[sock_id].iod.fit_residency_acc, mtbl_prev_socket[sock_id].iod.fit_residency_acc},
			};
			for (int i = 0; i < sizeof(residencies) / sizeof(residencies[0]); i++) {
				printf("    %-18s %-15u\n", residencies[i].name, calculate_acc_counter_32bit(residencies[i].curr, residencies[i].prev));
			}
		} else {
			struct { const char *name; uint32_t value; } residencies[] = {
				{"Prochot", mtbl_current_socket[sock_id].iod.prochot_residency_acc},
				{"Ppt", mtbl_current_socket[sock_id].iod.ppt_residency_acc},
				{"Thm", mtbl_current_socket[sock_id].iod.thm_residency_acc},
				{"VrHot", mtbl_current_socket[sock_id].iod.vrhot_residency_acc},
				{"CpuTdc", mtbl_current_socket[sock_id].iod.cpu_tdc_residency_acc},
				{"SocTdc", mtbl_current_socket[sock_id].iod.soc_tdc_residency_acc},
				{"IoMemTdc", mtbl_current_socket[sock_id].iod.io_mem_tdc_residency_acc},
				{"Fit", mtbl_current_socket[sock_id].iod.fit_residency_acc},
			};
			for (int i = 0; i < sizeof(residencies) / sizeof(residencies[0]); i++) {
				printf("    %-18s %u\n", residencies[i].name, residencies[i].value);
			}
		}
		printf("\n");

		/* Core Table Header */
		printf("%-20s\n", "  CORE");
		if (metric_table_format) {
			printf("    %-3s %-4s %-4s %8s %8s %8s %10s %13s %8s\n",
				"CCD#", "CORE", "APICID", "C0(%)", "CC1(%)", "CC6(%)", "FREQ(MHz)", "FREQ_EFF(MHz)", "POWER(W)");
		}
		else
		{
			printf("    %-3s %-4s %-4s %20s %20s %20s %20s %20s %20s\n",
				"CCD#", "CORE", "APICID", "C0", "CC1", "CC6", "FREQ", "FREQ_EFF", "POWER");
		}
		/* Core Table Data */
		for (int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++) {
			int loopCountToDisplay = 0;
			for (int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
			{
				if (-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;

				int apic_id = mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount];
				if (metric_table_format) {
					double c0 = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_c0[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_c0[loopCount]) / acc_counter_delta * fraction_uq10;
					double cc1 = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_cc1[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_cc1[loopCount]) / acc_counter_delta * fraction_uq10;
					double cc6 = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_cc6[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_cc6[loopCount]) / acc_counter_delta * fraction_uq10;
					double freq = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_frequency[loopCount]) / acc_counter_delta * fraction_uq10 * GHZ_TO_MHZ;
					double freq_eff = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount]) / acc_counter_delta * fraction_uq10 * GHZ_TO_MHZ;
					double power = calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_power[loopCount],
						mtbl_prev_socket[sock_id].ccd[ccdCount].core_power[loopCount]) / acc_counter_delta * fraction_uq10;

					printf("    CCD%d %-4d %-6u %8.3f %8.3f %8.3f %10.3f %13.3f %8.3f\n",
						ccdCount, loopCountToDisplay, apic_id, c0, cc1, cc6, freq, freq_eff, power);
				} else {
					uint64_t c0 = mtbl_current_socket[sock_id].ccd[ccdCount].core_c0[loopCount];
					uint64_t cc1 = mtbl_current_socket[sock_id].ccd[ccdCount].core_cc1[loopCount];
					uint64_t cc6 = mtbl_current_socket[sock_id].ccd[ccdCount].core_cc6[loopCount];
					uint64_t freq = mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency[loopCount];
					uint64_t freq_eff = mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount];
					uint64_t power = mtbl_current_socket[sock_id].ccd[ccdCount].core_power[loopCount];

					printf("    CCD%d %-4d %-6u %20llu %20llu %20llu %20llu %20llu %20llu\n",
						ccdCount, loopCountToDisplay, apic_id,
						(unsigned long long)c0, (unsigned long long)cc1, (unsigned long long)cc6,
						(unsigned long long)freq, (unsigned long long)freq_eff, (unsigned long long)power);
				}
				loopCountToDisplay++;
			}
			printf("\n");
		}
	}
	if(log_to_file) {
		char temp_string[300];
		if(create_log_header) {
			sprintf(temp_string, "Socket:%d TimestampRaw,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d TimestampDate,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d TimestampTime,", sock_id);append_string(&log_file_header, temp_string);

			sprintf(temp_string, "Socket:%d NumOfActiveCCDs,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "AccumulationCounterDelta" : "AccumulationCounter");append_string(&log_file_header, temp_string);

			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "MaxSocketTemperatureAcc('C)" : "MaxSocketTemperatureAcc");append_string(&log_file_header, temp_string);

			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "SocketPowerLimit(W)" : "SocketPowerLimit");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "MaxSocketPowerLimit(W)" : "MaxSocketPowerLimit");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "SocketPowerAcc(W)" : "SocketPowerAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "CorePowerAcc(W)" : "CorePowerAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "UncorePowerAcc(W)" : "UncorePowerAcc");append_string(&log_file_header, temp_string);

			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "SocketEnergyAcc(KJ)" : "SocketEnergyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "CoreEnergyAcc(KJ)" : "CoreEnergyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "UncoreEnergyAcc(KJ)" : "UncoreEnergyAcc");append_string(&log_file_header, temp_string);

			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "FclkFrequencyAcc(MHz)" : "FclkFrequencyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "UclkFrequencyAcc(MHz)" : "UclkFrequencyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "DdrRateAcc(MHz)" : "DdrRateAcc");append_string(&log_file_header, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Lclk:%d %s,", sock_id, loopCount, metric_table_format ? "LclkFrequencyAcc(MHz)" : "LclkFrequencyAcc");append_string(&log_file_header, temp_string);
			}

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Fclk:%d %s,", sock_id, loopCount, metric_table_format ? "FclkFrequencyTable(MHz)" : "FclkFrequencyTable");append_string(&log_file_header, temp_string);
			}
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Uclk:%d %s,", sock_id, loopCount, metric_table_format ? "UclkFrequencyTable(MHz)" : "UclkFrequencyTable");append_string(&log_file_header, temp_string);
			}
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Ddr:%d %s,", sock_id, loopCount, metric_table_format ? "DdrRateTable(MHz)" : "DdrRateTable");append_string(&log_file_header, temp_string);
			}
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Lclk:%d %s,", sock_id, loopCount, metric_table_format ? "LclkFrequencyTable(MHz)" : "LclkFrequencyTable");append_string(&log_file_header, temp_string);
			}

			sprintf(temp_string, "Socket:%d MaxDfPstateRange,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d MinDfPstateRange,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d MaxLclkDpmRange,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d MinLclkDpmRange,", sock_id);append_string(&log_file_header, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Xgmi:%d %s,", sock_id, loopCount, metric_table_format ? "XgmiBitrate(GB/sec)" : "XgmiBitrate");append_string(&log_file_header, temp_string);
			}
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Xgmi:%d %s,", sock_id, loopCount, metric_table_format ? "XgmiReadBandwidth(GB/sec)" : "XgmiReadBandwidth");append_string(&log_file_header, temp_string);
			}
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Xgmi:%d %s,", sock_id, loopCount, metric_table_format ? "XgmiWriteBandwidth(GB/sec)" : "XgmiWriteBandwidth");append_string(&log_file_header, temp_string);
			}

			if(metric_table_format)
			{
				sprintf(temp_string, "Socket:%d %s,", sock_id, "SocketC0ResidencyAcc(%)");append_string(&log_file_header, temp_string);
			}
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "SocketC0ResidencyAccSumOfAllCores" : "SocketC0ResidencyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "SocketDFCstateResidencyAcc(%)" : "SocketDFCstateResidencyAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "DramReadBandwidthAcc(GB/sec)" : "DramReadBandwidthAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "DramWriteBandwidthAcc(GB/sec)" : "DramWriteBandwidthAcc");append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d %s,", sock_id, metric_table_format ? "DramMaxBandwidth(GB/sec)" : "DramMaxBandwidth");append_string(&log_file_header, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_PCIE; loopCount++)
			{
				sprintf(temp_string, "Socket:%d Pcie:%d %s,", sock_id, loopCount, metric_table_format ? "PcieBandwidthAcc(GB/sec)" : "PcieBandwidthAcc");append_string(&log_file_header, temp_string);
			}

			sprintf(temp_string, "Socket:%d ProchotResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d PptResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d ThmResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d VrHotResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d CpuTdcResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d SocTdcResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d IoMemTdcResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);
			sprintf(temp_string, "Socket:%d FitResidencyAcc,", sock_id);append_string(&log_file_header, temp_string);

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, "ApicIdOfThread0");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "C0(%)" : "C0");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "CC1(%)" : "CC1");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "CC6(%)" : "CC6");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "Freq(MHz)" : "Freq");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "FreqEff(MHz)" : "FreqEff");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
			{
				int loopCountToDisplay = 0;
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "Socket:%d CCD:%02d Core:%02d %s,", sock_id, ccdCount, loopCountToDisplay, metric_table_format ? "Power(W)" : "Power");append_string(&log_file_header, temp_string);
					loopCountToDisplay++;
				}
			}
		}
		sprintf(temp_string, "%llu,", mtbl_current_socket[sock_id].iod.timestamp);append_string(&log_file_data, temp_string);

		rawtime = (time_t)mtbl_current_socket[sock_id].iod.timestamp;time (&rawtime);timeinfo = localtime (&rawtime);
		char date_buffer[100];char time_buffer[100];
		strftime (date_buffer, 100, "%Y-%m-%d", timeinfo);
		strftime (time_buffer, 100, "%H:%M:%S", timeinfo);

		sprintf(temp_string, "%s,", date_buffer);append_string(&log_file_data, temp_string);
		sprintf(temp_string, "%s,", time_buffer);append_string(&log_file_data, temp_string);

		sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.num_active_ccds);append_string(&log_file_data, temp_string);
		if (metric_table_format) {
			sprintf(temp_string, "%u,", acc_counter_delta); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%3.3lf,", (calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.max_socket_temperature_acc, mtbl_prev_socket[sock_id].iod.max_socket_temperature_acc)/acc_counter_delta * fraction_q10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.socket_power_limit * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.max_socket_power_limit * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_power_acc, mtbl_prev_socket[sock_id].iod.socket_power_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.core_power_acc, mtbl_prev_socket[sock_id].iod.core_power_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.uncore_power_acc, mtbl_prev_socket[sock_id].iod.uncore_power_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%5.3lf,", ((mtbl_current_socket[sock_id].iod.socket_energy_acc * fraction_uq16)/KILO)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", ((mtbl_current_socket[sock_id].iod.core_energy_acc * fraction_uq16)/KILO)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", ((mtbl_current_socket[sock_id].iod.uncore_energy_acc * fraction_uq16)/KILO)); append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.fclk_frequency_acc, mtbl_prev_socket[sock_id].iod.fclk_frequency_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.uclk_frequency_acc, mtbl_prev_socket[sock_id].iod.uclk_frequency_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.ddr_rate_acc, mtbl_prev_socket[sock_id].iod.ddr_rate_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.lclk_frequency_acc[loopCount], mtbl_prev_socket[sock_id].iod.lclk_frequency_acc[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.fclk_frequency_table[loopCount] * fraction_uq10)), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.uclk_frequency_table[loopCount] * fraction_uq10)), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.ddr_rate_table[loopCount] * fraction_uq10)), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.lclk_frequency_table[loopCount] * fraction_uq10)), append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_df_pstate_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.min_df_pstate_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_lclk_dpm_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.min_lclk_dpm_range); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_bit_rate[loopCount], mtbl_prev_socket[sock_id].iod.xgmi_bit_rate[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_read_bandwidth[loopCount], mtbl_prev_socket[sock_id].iod.xgmi_read_bandwidth[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.xgmi_write_bandwidth[loopCount], mtbl_prev_socket[sock_id].iod.xgmi_write_bandwidth[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);

			uint32_t total_cores_enabled = 1;
			esmi_number_of_physicalcores_in_socket_get(sock_id, &total_cores_enabled);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_c0_residency_acc, mtbl_prev_socket[sock_id].iod.socket_c0_residency_acc)/acc_counter_delta * fraction_uq10 * 100) / total_cores_enabled); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_c0_residency_acc, mtbl_prev_socket[sock_id].iod.socket_c0_residency_acc)/acc_counter_delta * fraction_uq10 * 100)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.socket_df_cstate_residency_acc, mtbl_prev_socket[sock_id].iod.socket_df_cstate_residency_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.dram_read_bandwidth_acc, mtbl_prev_socket[sock_id].iod.dram_read_bandwidth_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.dram_write_bandwidth_acc, mtbl_prev_socket[sock_id].iod.dram_write_bandwidth_acc)/acc_counter_delta * fraction_uq10)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%5.3lf,", (mtbl_current_socket[sock_id].iod.max_dram_bandwidth * fraction_uq10)); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_PCIE; loopCount++)
				sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].iod.pcie_bandwidth_acc[loopCount], mtbl_prev_socket[sock_id].iod.pcie_bandwidth_acc[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.prochot_residency_acc, mtbl_prev_socket[sock_id].iod.prochot_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.ppt_residency_acc, mtbl_prev_socket[sock_id].iod.ppt_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.thm_residency_acc, mtbl_prev_socket[sock_id].iod.thm_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.vrhot_residency_acc, mtbl_prev_socket[sock_id].iod.vrhot_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.cpu_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.cpu_tdc_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.soc_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.soc_tdc_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.io_mem_tdc_residency_acc, mtbl_prev_socket[sock_id].iod.io_mem_tdc_residency_acc)); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", calculate_acc_counter_32bit(mtbl_current_socket[sock_id].iod.fit_residency_acc, mtbl_prev_socket[sock_id].iod.fit_residency_acc)); append_string(&log_file_data, temp_string);

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_c0[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_c0[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_cc1[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_cc1[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_cc6[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_cc6[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_frequency[loopCount])/acc_counter_delta * fraction_uq10 * GHZ_TO_MHZ)), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount])/acc_counter_delta * fraction_uq10 * GHZ_TO_MHZ)), append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++)
				{
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%5.3lf,", (calculate_acc_counter_64bit(mtbl_current_socket[sock_id].ccd[ccdCount].core_power[loopCount], mtbl_prev_socket[sock_id].ccd[ccdCount].core_power[loopCount])/acc_counter_delta * fraction_uq10)), append_string(&log_file_data, temp_string);
				}

		} else {
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.accumulation_counter); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.max_socket_temperature_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.socket_power_limit); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_socket_power_limit); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.socket_power_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.core_power_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.uncore_power_acc); append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.socket_energy_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.core_energy_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.uncore_energy_acc); append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.fclk_frequency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.uclk_frequency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.ddr_rate_acc); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.lclk_frequency_acc[loopCount]), append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.fclk_frequency_table[loopCount]), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.uclk_frequency_table[loopCount]), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.ddr_rate_table[loopCount]), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_FREQ_TABLE_SIZE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.lclk_frequency_table[loopCount]), append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_df_pstate_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.min_df_pstate_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_lclk_dpm_range); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.min_lclk_dpm_range); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_bit_rate[loopCount]), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_read_bandwidth[loopCount]), append_string(&log_file_data, temp_string);
			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_XGMI; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.xgmi_write_bandwidth[loopCount]), append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.socket_c0_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.socket_df_cstate_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.dram_read_bandwidth_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.dram_write_bandwidth_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.max_dram_bandwidth); append_string(&log_file_data, temp_string);

			for(int loopCount = 0; loopCount < F1A_M50_M5F_MAX_PCIE; loopCount++)
				sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].iod.pcie_bandwidth_acc[loopCount]), append_string(&log_file_data, temp_string);

			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.prochot_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.ppt_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.thm_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.vrhot_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.cpu_tdc_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.soc_tdc_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.io_mem_tdc_residency_acc); append_string(&log_file_data, temp_string);
			sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].iod.fit_residency_acc); append_string(&log_file_data, temp_string);

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%u,", mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", 	(unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_c0[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_cc1[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_cc6[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_frequency_effective[loopCount]);
					append_string(&log_file_data, temp_string);
				}

			for(int ccdCount = 0; ccdCount < maxCCDsAvailable; ccdCount++)
				for(int loopCount = 0; loopCount < max_cores_per_ccd; loopCount++) {
					if(-1 == mtbl_current_socket[sock_id].ccd[ccdCount].core_apicid_of_thread0[loopCount]) continue;
					sprintf(temp_string, "%llu,", (unsigned long long)mtbl_current_socket[sock_id].ccd[ccdCount].core_power[loopCount]);
					append_string(&log_file_data, temp_string);
				}
		}
	}

	mtbl_prev_socket[sock_id] = mtbl_current_socket[sock_id];
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_show_metric_table(uint8_t sock_id, uint64_t sleep_time_in_milliseconds, bool initialize_smu_data, bool only_display_results)
{
	uint32_t met_ver;
	esmi_status_t ret;

	ret = esmi_metrics_table_version_get(&met_ver);
	if (ret != ESMI_SUCCESS) {
		printf("Socket:%d, Failed to get Metric Table Version, Err[%d]: %s\n", sock_id, ret, esmi_get_err_msg(ret));
		return ret;
	}
	/* Family 0x1A Model 0x50 to 0x5F have different metric table structure */
	if((sys_info.family == 0x1A) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
	{
		if((0x00700000 == met_ver))
			return epyc_show_metric_table_F1A_M50_M5F_ver_0x00700000(sock_id, met_ver, sleep_time_in_milliseconds, initialize_smu_data, only_display_results);
	}
	else if((sys_info.family == 0x19) && ((sys_info.model >= 0x90) && (sys_info.model <= 0x9F)))
		return epyc_show_metric_table_F1A_M90_M9F(sock_id, initialize_smu_data, only_display_results);

	return ESMI_NOT_SUPPORTED;
}
static int epyc_get_pwr_efficiency_mode(uint8_t sock_ind)
{
	esmi_status_t ret;
	uint8_t mode;
	uint32_t util = -1, ppt_limit = -1;
	char temp_string[300];
	char power_efficiency_mode_name[50];

	ret = esmi_pwr_efficiency_mode_get(sock_ind, &mode, &util, &ppt_limit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get power efficiency mode for socket [%d] Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(mode == 0) 	   sprintf(power_efficiency_mode_name, "HighPerformance");
	else if(mode == 1) sprintf(power_efficiency_mode_name, "PowerEfficiency");
	else if(mode == 2) sprintf(power_efficiency_mode_name, "IOPerformance");
	else if(mode == 3) sprintf(power_efficiency_mode_name, "BalancedMemory");
	else if(mode == 4) sprintf(power_efficiency_mode_name, "BalancedCore");
	else if(mode == 5) sprintf(power_efficiency_mode_name, "BalancedCoreMemory");
	else 			   sprintf(power_efficiency_mode_name, "Unknown");

	if(log_to_file && create_log_header) {
		if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
			sprintf(temp_string, "Socket:%d PowerEfficiencyMode,Socket:%d PowerEfficiencyModeName,Socket:%d PowerEfficiencyUtil(%%),Socket:%d PowerEfficiencyPptLimit(Watts),", sock_ind, sock_ind, sock_ind, sock_ind);
		else
			sprintf(temp_string, "Socket:%d PowerEfficiencyMode,Socket:%d PowerEfficiencyModeName,", sock_ind, sock_ind);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("-----------------------------------------------------------------\n");
		printf("| Socket[%d] PowerEfficiencyMode            | %d\t\t\t|\n", sock_ind, mode);
		printf("| Socket[%d] PowerEfficiencyModeName        | %s\t|\n", sock_ind, power_efficiency_mode_name);
		if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
		{
			printf("| Socket[%d] PowerEfficiencyUtil(%%)         | %d\t\t%s|\n", sock_ind, util, (util>=100)?"":"\t");
			printf("| Socket[%d] PowerEfficiencyPptLimit(Watts) | %.3f\t\t|\n", sock_ind, (double)ppt_limit/1000);
		}
		printf("-----------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
	{
		if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
			printf("Socket,PowerEfficiencyMode,PowerEfficiencyModeName,PowerEfficiencyUtil(%%),PowerEfficiencyPptLimit(Watts)\n%d,%d,%s,%d,%.3f\n", sock_ind, mode, power_efficiency_mode_name, util, (double)ppt_limit/1000);
		else
			printf("Socket,PowerEfficiencyMode,PowerEfficiencyModeName\n%d,%d,%s\n", sock_ind, mode, power_efficiency_mode_name);
	}
	else if(print_results == PRINT_RESULTS_AS_JSON)
	{
		if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerEfficiencyMode\":%d,\n\t\t\"PowerEfficiencyModeName\":\"%s\",\n\t\t\"PowerEfficiencyUtil(%%)\":%d,\n\t\t\"PowerEfficiencyPptLimit(Watts)\":%.3f\n\t},", sock_ind, mode, power_efficiency_mode_name, util, (double)ppt_limit/1000);
		else
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PowerEfficiencyMode\":%d,\n\t\t\"PowerEfficiencyModeName\":\"%s\"\n\t},", sock_ind, mode, power_efficiency_mode_name);
	}

	if(log_to_file)
	{
		if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
			sprintf(temp_string, "%d,%s,%d,%.3f,", mode, power_efficiency_mode_name, util, (double)ppt_limit/1000);
		else if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
			sprintf(temp_string, "%d,%s,%d,%d,", mode, power_efficiency_mode_name, -1, -1);
		else
			sprintf(temp_string, "%d,%s,", mode, power_efficiency_mode_name);
		append_string(&log_file_data, temp_string);
	}

	return ret;
}

static int epyc_set_xgmi_pstate_range(uint8_t sock_ind, uint8_t min, uint8_t max)
{
	esmi_status_t ret;

	ret = esmi_xgmi_pstate_range_set(sock_ind, min, max);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set xGMI pstate range, socket[%d], Min[%d], Max[%d], Err[%d]: %s\n",
			sock_ind, min, max, ret, esmi_get_err_msg(ret));
		if(min < max)
			printf("Note: Please make sure Min >= Max\n");
		return ret;
	}
	printf("Socket[%d] xGMI pstate range set to Min[%d] and Max[%d] successfully\n", sock_ind, min, max);

	return ret;

}

static int epyc_get_xgmi_pstate_range(uint8_t sock_ind)
{
	esmi_status_t ret;
	uint8_t min; uint8_t max;
	char temp_string[300];

	ret = esmi_xgmi_pstate_range_get(sock_ind, &min, &max);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set xGMI pstate range for socket [%d], Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d MinXgmiPstate,Socket:%d MaxXgmiPstate,", sock_ind, sock_ind);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("------------------------------------------\n");
		printf("| Socket[%d] MinXgmiPstate\t | %d\t |\n", sock_ind, min);
		printf("| Socket[%d] MaxXgmiPstate\t | %d\t |\n", sock_ind, max);
		printf("------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,MinXgmiPstate,MaxXgmiPstate\n%d,%d,%d\n", sock_ind, min, max);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"MinXgmiPstate\":%d,\n\t\t\"MaxXgmiPstate\":%d\n\t},", sock_ind, min, max);

	if(log_to_file)
	{
		sprintf(temp_string, "%d,%d,", min, max);
		append_string(&log_file_data, temp_string);
	}
	return ret;
}

static int epyc_get_cpu_iso_freq_policy(uint8_t sock_ind)
{
	esmi_status_t ret;
	bool val;
	char temp_string[300];

	ret = esmi_cpurail_isofreq_policy_get(sock_ind, &val);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get Cpu Iso frequency policy for socket [%d], Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d CpuIsoFrequencyPolicy,", sock_ind);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("--------------------------------------------------\n");
		printf("| Socket[%d] CpuIsoFrequencyPolicy\t | %d\t |\n", sock_ind, val);
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,CpuIsoFrequencyPolicy\n%d,%d\n", sock_ind, val);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"CpuIsoFrequencyPolicy\":%u\n\t},", sock_ind, val);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", val);
		append_string(&log_file_data, temp_string);
	}
	return ret;
}

static int epyc_set_cpu_iso_freq_policy(uint8_t sock_ind, uint8_t input)
{
	esmi_status_t ret;
	bool val;

	if (input > 1) {
		printf("Input value should be 0 or 1\n");
		return ESMI_INVALID_INPUT;
	}
	val = input;
	ret = esmi_cpurail_isofreq_policy_set(sock_ind, &val);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set Cpu Iso frequency policy for socket [%d], Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}
	printf("Socket[%d] Cpu Iso frequency policy is set to %d successfully\n", sock_ind, val);

	return ret;
}

static int epyc_set_dfc_ctrl(uint8_t sock_ind, uint8_t input)
{
	esmi_status_t ret;
	bool val;

	if (input > 1) {
		printf("Input value should be 0 or 1\n");
		return ESMI_INVALID_INPUT;
	}
	val = input;
	ret = esmi_dfc_enable_set(sock_ind, &val);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to set Data Fabric C-state enable control for socket [%d] Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}
	printf("Data Fabric C-state enable control is set successfully to %d\n", val);

	return ret;
}

static int epyc_get_dfc_ctrl(uint8_t sock_ind)
{
	esmi_status_t ret;
	bool val;
	char temp_string[300];

	ret = esmi_dfc_ctrl_setting_get(sock_ind, &val);
	if (ret != ESMI_SUCCESS) {
		printf("Failed to get Data Fabric C-state enable control for socket [%d] Err[%d]: %s\n",
			sock_ind, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d DataFabricCStateEnable,", sock_ind);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("--------------------------------------------------\n");
		printf("| Socket[%d] DataFabricCStateEnable\t | %d\t |\n", sock_ind, val);
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,DataFabricCStateEnable\n%d,%d\n", sock_ind, val);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"DataFabricCStateEnable\":%u\n\t},", sock_ind, val);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", val);
		append_string(&log_file_data, temp_string);
	}
	return ret;
}

static esmi_status_t epyc_set_pc6_enable(uint32_t sock_id, uint8_t pc6_enable)
{
	esmi_status_t ret;

	ret = esmi_pc6_enable_set(sock_id, pc6_enable);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] PC6 enable\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("PC6 Enable is set to [%d] on socket[%d] successfully\n",
		pc6_enable, sock_id);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_pc6_enable(uint32_t sock_id)
{
	esmi_status_t ret;
	uint8_t pc6_enable;
	char temp_string[300];

	ret = esmi_pc6_enable_get(sock_id, &pc6_enable);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] PC6 Enable\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d PC6Enable,", sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("----------------------------------\n");
		printf("| Socket[%d] PC6Enable\t | %d\t |\n", sock_id, pc6_enable);
		printf("----------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,PC6Enable\n%d,%d\n", sock_id, pc6_enable);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"PC6Enable\":%u\n\t},", sock_id, pc6_enable);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", pc6_enable);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_cc6_enable(uint32_t sock_id, uint8_t cc6_enable)
{
	esmi_status_t ret;

	ret = esmi_cc6_enable_set(sock_id, cc6_enable);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] CC6 Enable\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("CC6 Enable is set to [%d] on socket[%d] successfully\n",
		cc6_enable, sock_id);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_cc6_enable(uint32_t sock_id)
{
	esmi_status_t ret;
	uint8_t cc6_enable;
	char temp_string[300];

	ret = esmi_cc6_enable_get(sock_id, &cc6_enable);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] CC6 Enable\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d CC6Enable,", sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("----------------------------------\n");
		printf("| Socket[%d] CC6Enable\t | %d\t |\n", sock_id, cc6_enable);
		printf("----------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,CC6Enable\n%d,%d\n", sock_id, cc6_enable);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"CC6Enable\":%u\n\t},", sock_id, cc6_enable);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", cc6_enable);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_set_sdps_limit(uint32_t sock_id, uint32_t sdps_limit)
{
	esmi_status_t ret;
	uint32_t sdps_limit_temp = sdps_limit;

	ret = esmi_sdps_limit_set(sock_id, &sdps_limit_temp);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to set socket[%d] SDPSLimit\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	if(0 == sdps_limit_temp) sdps_limit_temp = sdps_limit;
	printf("Socket:%d, set to SDPSLimit(Watts):%.3f, successfully\n", sock_id, (double)sdps_limit_temp/1000);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_sdps_limit(uint32_t sock_id)
{
	esmi_status_t ret;
	uint32_t sdps_limit;
	char temp_string[300];

	ret = esmi_sdps_limit_get(sock_id, &sdps_limit);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] SDPSLimit\n", sock_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d SDPSLimit(Watts),", sock_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("--------------------------------------------------\n");
		printf("| Socket[%d] SDPSLimit(Watts)\t | %.3f\t |\n", sock_id, (double)sdps_limit/1000);
		printf("--------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,SDPSLimit(Watts)\n%d,%.3f\n", sock_id, (double)sdps_limit/1000);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"SDPSLimit(Watts)\":%.3f\n\t},", sock_id, (double)sdps_limit/1000);

	if(log_to_file)
	{
		sprintf(temp_string, "%.3f,", (double)sdps_limit/1000);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_ccd_power(uint32_t core_id)
{
	esmi_status_t ret;
	uint32_t power;
	char temp_string[300];

	ret = esmi_read_ccd_power(core_id, &power);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get power of CCD to which core[%d] belongs.\n", core_id);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Core:%d CCDPower(Watts),", core_id);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("-------------------------------------------------\n");
		printf("| Core[%d] CCDPower(Watts)\t | %.3f\t|\n", core_id, (double)power/1000);
		printf("-------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Core,CCDPower(Watts)\n%u,%.3f\n", core_id, (double)power/1000);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Core\":%u,\n\t\t\"CCDPower(Watts)\":%.3f\n\t},", core_id, (double)power/1000);

	if(log_to_file)
	{
		sprintf(temp_string, "%.3f,", (double)power/1000);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_tdelta(uint32_t sock_ind)
{
	esmi_status_t ret;
	uint8_t tdelta;
	char temp_string[300];

	ret = esmi_read_tdelta(sock_ind, &tdelta);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] tdelta\n", sock_ind);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d TDelta,", sock_ind);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("--------------------------------------------------------------------------\n");
		if (tdelta == 0)
			printf("| Socket[%d] TDelta \t | %u (%s)\t |\n", sock_ind, tdelta, "Thermal solution behaviour normal");
		else
			printf("| Socket[%d] TDelta \t | %u (%s)\t |\n", sock_ind, tdelta, "Thermal solution behaviour out of range");
		printf("--------------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,TDelta\n%d,%d\n", sock_ind, tdelta);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"TDelta\":%u\n\t},", sock_ind, tdelta);

	if(log_to_file)
	{
		sprintf(temp_string, "%u,", tdelta);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}
static esmi_status_t epyc_read_dimm_sb_reg(uint32_t sock_ind, uint64_t dimm_addr, uint64_t lid, uint64_t reg_offset, uint64_t reg_space)
{
	esmi_status_t ret;
	struct dimm_sb_info dimm_sb_info_;dimm_sb_info_.m_dimm_sb_info_inarg.reg_value = 0;
	char temp_string[300];

	if((dimm_addr > MAX_SPD_DIMM_ADDRESS) || (lid > MAX_SPD_LID_ADDRESS) || (reg_offset > MAX_SPD_REG_OFFSET) || (reg_space > MAX_SPD_REG_SPACE))
	{
		ret = ESMI_INVALID_INPUT;

		printf("Failed: DimmSbData to get on Socket[%d] DimmAddress[0x%lx] Lid[0x%lx] Offset[0x%lx] RegSpace[%ld]\n", sock_ind, dimm_addr, lid, reg_offset, reg_space);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr = dimm_addr;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.lid = lid;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset = reg_offset;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space = reg_space;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.write_data = 0;//Not Used for Read, so initialize to 0

	ret = esmi_dimm_sb_reg_read(sock_ind, &dimm_sb_info_);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: DimmSbData to get on Socket[%d] DimmAddress[0x%x] Lid[0x%x] Offset[0x%x] RegSpace[%d]\n", sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if(log_to_file && create_log_header) {
		sprintf(temp_string, "Socket:%d DimmAddress:0x%x Lid:0x%x Offset:0x%x RegSpace:%d DimmSbData,", sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space);
		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		printf("------------------------------------------------------------------------------------------\n");
		printf("| Socket[%d] DimmAddress[0x%x] Lid[0x%x] Offset[0x%x] RegSpace[%d] DimmSbData | 0x%-2x\t |\n", sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space, dimm_sb_info_.read_data);
		printf("------------------------------------------------------------------------------------------\n");
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
		printf("Socket,DimmAddress,Lid,Offset,RegSpace,DimmSbData\n%d,0x%x,0x%x,0x%x,%d,0x%x\n", sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space, dimm_sb_info_.read_data);
	else if(print_results == PRINT_RESULTS_AS_JSON)
		printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Dimm address\":\"0x%x\",\n\t\t\"Lid\":\"0x%x\",\n\t\t\"Offset\":\"0x%x\",\n\t\t\"RegSpace\":%d,\n\t\t\"DimmSbData\":\"0x%x\"\n\t},", sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space, dimm_sb_info_.read_data);

	if(log_to_file)
	{
		sprintf(temp_string, "0x%x,", dimm_sb_info_.read_data);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static esmi_status_t epyc_write_dimm_sb_reg(uint32_t sock_ind, uint64_t dimm_addr, uint64_t lid, uint64_t reg_offset, uint64_t reg_space, uint64_t write_data)
{
	esmi_status_t ret;
	struct dimm_sb_info dimm_sb_info_;dimm_sb_info_.m_dimm_sb_info_inarg.reg_value = 0;

	if((dimm_addr > MAX_SPD_DIMM_ADDRESS) || (lid > MAX_SPD_LID_ADDRESS) || (reg_offset > MAX_SPD_REG_OFFSET) || (reg_space > MAX_SPD_REG_SPACE) || (write_data > MAX_SPD_WRITE_DATA))
	{
		ret = ESMI_INVALID_INPUT;

		printf("Failed: DimmSbData to set [0x%-2lx] on Socket[%d] DimmAddress[0x%lx] Lid[0x%lx] Offset[0x%lx] RegSpace[%ld]\n", write_data, sock_ind, dimm_addr, lid, reg_offset, reg_space);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr = dimm_addr;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.lid = lid;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset = reg_offset;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space = reg_space;
	dimm_sb_info_.m_dimm_sb_info_inarg.info.write_data = write_data;

	ret = esmi_dimm_sb_reg_write(sock_ind, &dimm_sb_info_);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: DimmSbData to set [0x%-2x] on Socket[%d] DimmAddress[0x%x] Lid[0x%x] Offset[0x%x] RegSpace[%d]\n", dimm_sb_info_.m_dimm_sb_info_inarg.info.write_data, sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	printf("\nSetDimmSbData to set [0x%-2x] on Socket[%d] DimmAddress[0x%x] Lid[0x%x] Offset[0x%x] RegSpace[%d] successfully\t\n", dimm_sb_info_.m_dimm_sb_info_inarg.info.write_data, sock_ind, dimm_sb_info_.m_dimm_sb_info_inarg.info.dimm_addr, dimm_sb_info_.m_dimm_sb_info_inarg.info.lid, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_offset, dimm_sb_info_.m_dimm_sb_info_inarg.info.reg_space);

	return ESMI_SUCCESS;
}

static esmi_status_t epyc_get_svi3_vr_temp(uint32_t sock_ind, uint8_t svi3_rail_selection, uint8_t rail_index)
{
	esmi_status_t ret;
	struct svi3_info svi3_info_;svi3_info_.m_svi3_info_inarg.reg_value = 0;
	char temp_string[300];
	char svi3_rail_string[20];

	svi3_info_.m_svi3_info_inarg.info.svi3_rail_selection = svi3_rail_selection;
	if (svi3_rail_selection)
	{
		if((rail_index > MAX_SVI3_RAIL_INDEX)|| (svi3_rail_selection > MAX_SVI3_RAIL_SELECTION))
		{
			ret = ESMI_INVALID_INPUT;

			printf("Failed: to get socket[%d] SVI3 VR controller temperature\n", sock_ind);
			printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
			return ret;
		}
		svi3_info_.m_svi3_info_inarg.info.svi3_rail_index = rail_index;
	}

	ret = esmi_get_svi3_vr_controller_temp(sock_ind, &svi3_info_);
	if (ret != ESMI_SUCCESS) {
		printf("Failed: to get socket[%d] SVI3 VR controller temperature\n", sock_ind);
		printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	if (svi3_info_.m_svi3_info_inarg.info.svi3_rail_index < ARRAY_SIZE(svi3_rail_index_names)) {
		sprintf(svi3_rail_string, "%s", svi3_rail_index_names[svi3_info_.m_svi3_info_inarg.info.svi3_rail_index]);
	} else {
		sprintf(svi3_rail_string, "Unknown_Rail");
	}

	if(log_to_file && create_log_header) {
		if (!svi3_rail_selection)
			sprintf(temp_string, "Socket:%d HottestSvi3VrRail,Socket:%d HottestSvi3VrRailName,Socket:%d HottestSvi3VrTemperature('C),", sock_ind, sock_ind, sock_ind);
		else
			sprintf(temp_string, "Socket:%d Svi3VrRail:%d Svi3VrRailName:%s Svi3VrTemperature('C),", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string);

		append_string(&log_file_header, temp_string);
	}

	if(print_results == PRINT_RESULTS)
	{
		if (!svi3_rail_selection)
		{
			printf("----------------------------------------------------------------------------------------------------------\n");
			printf("| Socket[%d] HottestSvi3VrRail[%d] HottestSvi3VrRailName[%s] HottestSvi3VrTemperature('C)\t | %d\t |\n", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
			printf("----------------------------------------------------------------------------------------------------------\n");
		}
		else
		{
			printf("------------------------------------------------------------------------------------------\n");
			printf("| Socket[%d] Svi3VrRail[%d] Svi3VrRailName[%s] Svi3VrTemperature('C)\t%s | %d\t |\n", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, (svi3_info_.m_svi3_info_inarg.info.svi3_rail_index==3)?"\t":"", svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
			printf("------------------------------------------------------------------------------------------\n");
		}
	}
	else if(print_results == PRINT_RESULTS_AS_CSV)
	{
		if (!svi3_rail_selection)
			printf("Socket,HottestSvi3VrRail,HottestSvi3VrRailName,HottestSvi3VrTemperature('C)\n%d,%d,%s,%d\n", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
		else
			printf("Socket,Svi3VrRail,Svi3VrRailName,Svi3VrTemperature('C)\n%d,%d,%s,%d\n", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
	}
	else if(print_results == PRINT_RESULTS_AS_JSON)
	{
		if (!svi3_rail_selection)
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"HottestSvi3VrRail\":%d,\n\t\t\"HottestSvi3VrRailName\":\"%s\",\n\t\t\"HottestSvi3VrTemperature('C)\":%d\n\t},", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
		else
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"Svi3VrRail\":%d,\n\t\t\"Svi3VrRailName\":\"%s\",\n\t\t\"Svi3VrTemperature('C)\":%d\n\t},", sock_ind, svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
	}

	if(log_to_file)
	{
		if (!svi3_rail_selection)
			sprintf(temp_string, "%d,%s,%d,", svi3_info_.m_svi3_info_inarg.info.svi3_rail_index, svi3_rail_string, svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
		else
			sprintf(temp_string, "%d,", svi3_info_.m_svi3_info_inarg.info.svi3_temperature);
		append_string(&log_file_data, temp_string);
	}
	return ESMI_SUCCESS;
}

static char* const read_enabled_commands_F_0x1A_M_0x50_0x5F[3][32] = {
	{"", 		"01h:TestMessage", 			 "02h:GetSmuVersion", 	  "03h:GetInterfaceVersion",/* 00h-03h */
	 "04h:ReadSocketPower", "", "06h:ReadSocketPowerLimit", "07h:ReadMaxSocketPowerLimit",/* 04h-07h */
	 "", "", "0Ah:ReadBoostLimit",     "0Bh:ReadProchotStatus",/* 08h-0Bh */
	 "0Ch:SetXgmiLinkWidthRange", "0Dh:APBDisable", 				"", 		  "0Fh:ReadCurrentFclkMemclk",/* 0Ch-0Fh */
	 "10h:ReadCclkFrequencyLimit", "11h:ReadSocketC0Residency", "", "13h:GetLclkDpmLevelRange",/* 10h-13h */
	 "14h:GetMaxDDRBandwidthAndUtilization", "", 	"16h:GetDIMMTemperatureAndRefreshRate", "17h:GetDIMMPowerConsumption",/* 14h-17h */
	 "18h:GetDIMMThermalSensor", "19h:PwrCurrentActiveFreqLimitSocket", "1Ah:PwrCurrentActiveFreqLimitCore", "1Bh:PwrSviTelemetryAllRails",/* 18h-1Bh */
	 "1Ch:GetSocketFrequencyRange", "1Dh:GetCurrentIoBandwidth", "1Eh:GetCurrentXgmiBandwidth", "",/* 1Ch-1Fh */
	},
	{"", "21h:PwrEfficiencyModeSelection", "22h:DfPstateRange", "23h:GetMetricsVersion",/* 20h-23h */
	 "24h:GetMetricsTable", "25h:GetMetricsTableDramAddressAndSize", "26h:XgmiPstateRange", "27h:CpuRailIsoFreqPolicy",/* 24h-27h */
	 "28h:DfcEnable", "29h:PC6Enable", "2Ah:CC6Enable", "",/* 28h-2Bh */
	 "", "", "", "",/* 2Ch-2Fh */
	 "30h:GetRaplUnits","31h:GetRaplCoreCounter", "32h:GetRaplPackageCounter", "33h:DimmSbRd",/* 30h-33h */
	 "34h:ReadCCDPower","35h:ReadTdelta", "36h:GetSVI3VRControllerTemperature", "37h:GetEnabledHSMCommands",/* 34h-37h */
	 "38h:FloorLimit", "", "3Ah:SDPSLimit", "",/* 38h-3Bh */
	 "","", "", "",/* 3Ch-3Fh */
	},
	{"", "", "", "",/* 40h-43h */
	 "", "", "", "",/* 44h-47h */
	 "", "", "", "",/* 48h-4Bh */
	 "", "", "", "",/* 4Ch-4Fh */
	 "", "", "", "",/* 50h-53h */
	 "", "", "", "",/* 54h-57h */
	 "", "", "", "",/* 58h-5Bh */
	 "", "", "", "",/* 5Ch-5Fh */
	},
};
static char* const write_enabled_commands_F_0x1A_M_0x50_0x5F[3][32] = {
	{"", 		"", 			 "", 	  "",/* 00h-03h */
	 "", "05h:WriteSocketPowerLimit", "", "",/* 04h-07h */
	 "08h:WriteBoostLimit", "09h:WriteBoostLimitAllCores", "",     "",/* 08h-0Bh */
	 "0Ch:SetXgmiLinkWidthRange", "0Dh:APBDisable", 				"0Eh:APBEnable", 		  "",/* 0Ch-0Fh */
	 "", "", "12h:SetLclkDpmLevelRange", "",/* 10h-13h */
	 "", "", 	"", "",/* 14h-17h */
	 "", "", "", "",/* 18h-1Bh */
	 "", "", "", "",/* 1Ch-1Fh */
	},
	{"", "21h:PwrEfficiencyModeSelection", "22h:DfPstateRange", "",/* 20h-23h */
	 "", "", "26h:XgmiPstateRange", "27h:CpuRailIsoFreqPolicy",/* 24h-27h */
	 "28h:DfcEnable", "29h:PC6Enable", "2Ah:CC6Enable", "",/* 28h-2Bh */
	 "", "", "", "",/* 2Ch-2Fh */
	 "","", "", "",/* 30h-33h */
	 "","", "", "",/* 34h-37h */
	 "38h:FloorLimit", "39h:SpdSbWr", "3Ah:SDPSLimit", "",/* 38h-3Bh */
	 "","", "", "",/* 3Ch-3Fh */
	},
	{"", "", "", "",/* 40h-43h */
	 "", "", "", "",/* 44h-47h */
	 "", "", "", "",/* 48h-4Bh */
	 "", "", "", "",/* 4Ch-4Fh */
	 "", "", "", "",/* 50h-53h */
	 "", "", "", "",/* 54h-57h */
	 "", "", "", "",/* 58h-5Bh */
	 "", "", "", "",/* 5Ch-5Fh */
	},
};
static esmi_status_t epyc_get_enabled_commands(uint32_t sock_id)
{
	esmi_status_t ret;
	char temp_string[300];
	char temp_string1[300];
	char readwrite[20];

	struct hsmp_enabled_commands_info info;

	char* enabled_status_for_csv = NULL;
	for(int loop_count = 0; loop_count < 2; loop_count++)
	{
		info.read_mask = (loop_count == 0);
		strcpy(readwrite, (loop_count == 0) ? "Read" : "Write");
		info.arg0 = 0;info.arg1 = 0;info.arg2 = 0;

		ret = esmi_get_enabled_commands(sock_id, &info);
		if (ret != ESMI_SUCCESS) {
			printf("Failed: to get %sEnabledCommands of Socket[%d].\n", readwrite, sock_id);
			printf(RED "Err[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
			return ret;
		}
		if(log_to_file && create_log_header) {
			sprintf(temp_string, "Socket:%d %sEnabledCommandsBitMask0,Socket:%d %sEnabledCommandsBitMask1,Socket:%d %sEnabledCommandsBitMask2,", sock_id, readwrite, sock_id, readwrite, sock_id, readwrite);
			append_string(&log_file_header, temp_string);
		}

		if(print_results == PRINT_RESULTS)
			printf("Socket:%d, %sEnabledCommandsBitMask0:0x%X, %sEnabledCommandsBitMask1:0x%X, %sEnabledCommandsBitMask2:0x%X\n", sock_id, readwrite, info.arg0, readwrite, info.arg1, readwrite, info.arg2);
		else if(print_results == PRINT_RESULTS_AS_CSV)
			printf("Socket,%sEnabledCommandsBitMask0,%sEnabledCommandsBitMask1,%sEnabledCommandsBitMask2,", readwrite, readwrite, readwrite);
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t{\n\t\t\"Socket\":%d,\n\t\t\"%sEnabledCommandsBitMask0\":\"0x%X\",\n\t\t\"%sEnabledCommandsBitMask1\":\"0x%X\",\n\t\t\"%sEnabledCommandsBitMask2\":\"0x%X\",", sock_id, readwrite, info.arg0, readwrite, info.arg1, readwrite, info.arg2);

		bool json_prints = false;
		for(int arg_count = 0; arg_count < 3; arg_count++)
		{
			uint32_t arg_received = 0;
			if(arg_count == 0)		arg_received = info.arg0;
			else if(arg_count == 1)	arg_received = info.arg1;
			else if(arg_count == 2)	arg_received = info.arg2;

			for(int bit_count = 0; bit_count < 32; bit_count++)
			{
				const char *cmd = NULL;
				if (info.read_mask)
				{
					if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
						cmd = read_enabled_commands_F_0x1A_M_0x50_0x5F[arg_count][bit_count];
				}
				else
				{
					if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
						cmd = write_enabled_commands_F_0x1A_M_0x50_0x5F[arg_count][bit_count];
				}
				if (!cmd || cmd[0] == '\0') continue;

				if(print_results == PRINT_RESULTS)
					printf("\t%s:%s\n", cmd, (arg_received & (1 << bit_count)) ? "Enabled" : "Disabled");
				else if(print_results == PRINT_RESULTS_AS_CSV)
				{
					printf("%s,", cmd);

					sprintf(temp_string1, "%s,", (arg_received & (1 << bit_count)) ? "Enabled" : "Disabled");
					append_string(&enabled_status_for_csv, temp_string1);
				}
				else if(print_results == PRINT_RESULTS_AS_JSON)
				{
					printf("%s\n\t\t\"%s\": \"%s\"", json_prints ? "," : "", cmd, (arg_received & (1 << bit_count)) ? "Enabled" : "Disabled");
					json_prints = true;
				}
			}
		}
		if(print_results == PRINT_RESULTS)
			printf("\n");
		else if(print_results == PRINT_RESULTS_AS_CSV)
		{
			printf("\n%d,0x%X,0x%X,0x%X,%s\n", sock_id, info.arg0, info.arg1, info.arg2, enabled_status_for_csv);

			if(enabled_status_for_csv)
			{
				free(enabled_status_for_csv);
				enabled_status_for_csv = NULL;
			}
		}
		else if(print_results == PRINT_RESULTS_AS_JSON)
			printf("\n\t},");

		if(log_to_file)
		{
			sprintf(temp_string, "0x%X,0x%X,0x%X,", info.arg0, info.arg1, info.arg2);
			append_string(&log_file_data, temp_string);
		}
	}

	return ESMI_SUCCESS;
}

static char* const feat_comm[] = {
	"Output Option<s>:",
	"  -h, --help\t\t\t\t\t\t\tShow this help message",
	"  -A, --showall\t\t\t\t\t\t\tShow all esmi parameter values",
	"  -V  --version \t\t\t\t\t\tShow e-smi library version",
	"  --testmailbox [SOCKET] [VALUE<0-0xFFFFFFFF>]\t\t\tTest HSMP mailbox interface",
	"  --writemsrallowlist \t\t\t\t\t\tWrite msr-safe allowlist file",
	"  --json\t\t\t\t\t\t\tPrint output on console as json format[applicable only for get commands]",
	"  --csv\t\t\t\t\t\t\t\tPrint output on console as csv format[applicable only for get commands]",
	"  --initialdelay [INITIAL_DELAY] <TIME_RANGE<ms,s,m,h,d>>\tInitial delay before start of execution",
	"  --loopdelay    [LOOP_DELAY]    <TIME_RANGE<ms,s,m,h,d>>\tLoop delay before executing each loop",
	"  --loopcount    [LOOP_COUNT]\t\t\t\t\tSet the loop count to the specified value[pass \"-1\" for infinite loop]",
	"  --stoploop     [STOPLOOP_FILE_NAME]\t\t\t\tSet the StopLoop file name, loop will stop once the stoploop file is available",
	"  --printonconsole [ENABLE_PRINT<0-1>]\t\t\t\tPrint output on console if set to 1, or 0 to suppress the console output",
	"  --log [LOG_FILE_NAME]\t\t\t\t\t\tSet the Log file name, in which the data collected need to be logged\n",
};

static char* const feat_energy[] = {
	"Get Option<s>:",
	"  --showcoreenergy [CORE]\t\t\t\t\tShow energy for a given CPU (Joules)",
	"  --showsockenergy\t\t\t\t\t\tShow energy for all sockets (KJoules)",
};

static char* const feat_ver2_get[] = {
	"  --showsockpower\t\t\t\t\t\tShow power metrics for all sockets (Watts)",
	"  --showcorebl [CORE]\t\t\t\t\t\tShow BoostLimit for a given CPU (MHz)",
	"  --showsockc0res [SOCKET]\t\t\t\t\tShow C0Residency for a given socket (%)",
	"  --showsmufwver\t\t\t\t\t\tShow SMU FW Version",
	"  --showhsmpdriverver\t\t\t\t\t\tShow HSMP Driver Version",
	"  --showhsmpprotover\t\t\t\t\t\tShow HSMP Protocol Version",
	"  --showprochotstatus\t\t\t\t\t\tShow HSMP PROCHOT status for all sockets",
	"  --showclocks\t\t\t\t\t\t\tShow Clock Metrics (MHz) for all sockets",
};

static char* const feat_ver2_set[] = {
	"Set Option<s>:",
	"  --setpowerlimit [SOCKET] [POWER]\t\t\t\tSet power limit"
	" for a given socket (mWatts)",
	"  --setcorebl [CORE] [BOOSTLIMIT]\t\t\t\tSet BoostLimit for a given core (MHz)",
	"  --setsockbl [SOCKET] [BOOSTLIMIT]\t\t\t\tSet BoostLimit for a given Socket (MHz)",
	"  --apbdisable [SOCKET] [PSTATE<0-2>]\t\t\t\tSet Data Fabric"
	" Pstate for a given socket",
	"  --apbenable [SOCKET]\t\t\t\t\t\tEnable the Data Fabric performance"
	" boost algorithm for a given socket",
	"  --setxgmiwidth [SOCKET] [MIN<0-2>] [MAX<0-2>]\t\t\tSet xgmi link width"
	" in a multi socket system (MAX >= MIN)",
	"  --setlclkdpmlevel [SOCKET] [NBIOID<0-3>] [MIN<0-3>] [MAX<0-3>]Set lclk dpm level"
	" for a given nbio in a given socket (MAX >= MIN)"
};

static char* const feat_ver3[] = {
	"  --showddrbw\t\t\t\t\t\t\tShow DDR bandwidth details (Gbps)",
};

static char* const feat_ver4[] = {
	"  --showsockettemp\t\t\t\t\t\tShow Temperature monitor for all sockets ('C)",
};

static char* const feat_ver5_get[] = {
	"  --showdimmtemprange [SOCKET] [DIMM_ADDR]\t\t\tShow dimm temperature range and"
	" refresh rate for a given socket and dimm address",
	"  --showdimmthermal [SOCKET] [DIMM_ADDR]\t\t\tShow dimm thermal values for a given socket"
	" and dimm address",
	"  --showdimmpower [SOCKET] [DIMM_ADDR]\t\t\t\tShow dimm power consumption for a given socket"
	" and dimm address",
	"  --showcclkfreqlimit [CORE]\t\t\t\t\tShow current clock frequency limit(MHz) for a given core",
	"  --showsvipower \t\t\t\t\t\tShow svi based power telemetry of all rails for all sockets",
	"  --showiobw [SOCKET] [LINK<P0-P3,G0-G3>]\t\t\tShow IO aggregate bandwidth for a given socket and"
	" linkname",
	"  --showlclkdpmlevel [SOCKET] [NBIOID<0-3>]\t\t\tShow lclk dpm level for a given nbio"
	" in a given socket",
	"  --showsockclkfreqlimit [SOCKET]\t\t\t\tShow current clock frequency limit(MHz) for a given socket"
};

static char* const feat_ver5_F1A_M50_5F_get[] = {
	"  --showdimmtemprange [SOCKET] [DIMM_ADDR]\t\t\tShow dimm temperature range and"
	" refresh rate for a given socket and dimm address",
	"  --showdimmthermal [SOCKET] [DIMM_ADDR]\t\t\tShow dimm thermal values for a given socket"
	" and dimm address",
	"  --showmaxdimmthermal [SOCKET]\t\t\t\t\tShow hottest dimm thermal values for a given socket",
	"  --showdimmpower [SOCKET] [DIMM_ADDR]\t\t\t\tShow dimm power consumption for a given socket"
	" and dimm address",
	"  --showmaxdimmpower [SOCKET]\t\t\t\t\tShow highest dimm power consumption for a given socket",
	"  --showtotaldimmpower [SOCKET]\t\t\t\t\tShow total dimm power consumption for a given socket",
	"  --showcclkfreqlimit [CORE]\t\t\t\t\tShow current clock frequency limit(MHz) for a given core",
	"  --showsvipower \t\t\t\t\t\tShow svi based power telemetry of all rails for all sockets",
	"  --showiobw [SOCKET] [LINK<P0-P2,P4-P5,G0-G2>]\t\t\tShow IO aggregate bandwidth for a given socket and"
	" linkname",
	"  --showlclkdpmlevel [SOCKET] [NBIOID<0-3>]\t\t\tShow lclk dpm level for a given nbio"
	" in a given socket",
	"  --showsockclkfreqlimit [SOCKET]\t\t\t\tShow current clock frequency limit(MHz) for a given socket"
};

static char* const feat_ver5_F19_M00_0F_get[] = {
	"  --showxgmibw [SOCKET] [LINK<P0-P3,G0-G3>] [BW<AGG_BW,RD_BW,WR_BW>]\n\t\t\t\t\t\t\t\tShow xGMI bandwidth for a given socket,"
	" linkname and bwtype"
};

static char* const feat_ver5_F19_M00_0F_set[] = {
	"  --setpowerefficiencymode [SOCKET] [MODE<0-3>]\t\t\tSet power efficiency mode"
	" for a given socket",
};

static char* const feat_ver5_F1A_M00_1F_get[] = {
	"  --showxgmibw [SOCKET] [LINK<P1,P3,G0-G3>] [BW<AGG_BW,RD_BW,WR_BW>]\n\t\t\t\t\t\t\t\tShow xGMI bandwidth for a given socket,"
	" linkname and bwtype",
	"  --showcurrpwrefficiencymode [SOCKET]\t\t\t\tShow current power effciency mode",
	"  --showcpurailisofreqpolicy [SOCKET]\t\t\t\tShow current CPU ISO frequency policy",
	"  --showdfcstatectrl [SOCKET]\t\t\t\t\tShow current DF C-state status",
};

static char* const feat_ver5_xgmibw_F1A_M50_5F_get[] = {
	"  --showxgmibw [SOCKET] [LINK<P0-P2,G0-G2>] [BW<AGG_BW,RD_BW,WR_BW>]\n\t\t\t\t\t\t\t\tShow xGMI bandwidth for a given socket,"
	" linkname and bwtype",
	"  --showcurrpwrefficiencymode [SOCKET]\t\t\t\tShow current power effciency mode",
	"  --showcpurailisofreqpolicy [SOCKET]\t\t\t\tShow current CPU ISO frequency policy",
	"  --showdfcstatectrl [SOCKET]\t\t\t\t\tShow current DF C-state status",
};

static char* const feat_ver5_F1A_M00_1F_set[] = {
	"  --setpowerefficiencymode [SOCKET] [MODE<0-5>]\t\t\tSet power efficiency mode"
	" for a given socket",
	"  --setxgmipstaterange [SOCKET] [MIN<0,1>] [MAX<0,1>]\t\tSet xgmi pstate range (MAX <= MIN)",
	"  --setcpurailisofreqpolicy [SOCKET] [VAL<0,1>]\t\t\tSet CPU ISO frequency policy",
	"  --setdfcctrl [SOCKET] [VAL<0,1>]\t\t\t\tEnable or disable DF c-state"
};

static char* const feat_ver7_F1A_M50_5F_set[] = {
	"  --setpowerefficiencymode [SOCKET] [MODE<0-5>] [UTIL<0-100>(If MODE=4/5)] [PPTLimit(mW)(If MODE=4/5)]\n\t\t\t\t\t\t\t\tSet power efficiency mode"
	" for a given socket.\n\t\t\t\t\t\t\t\tIf Mode=4/5, UTIL(%)<0-100> and PPTLimit(mW) are mandatory",
	" \t\t\t\t\t\t\t\t\t0=HighPerformance,\n\t\t\t\t\t\t\t\t\t1=PowerEfficiency,\n\t\t\t\t\t\t\t\t\t2=IOPerformance,\n\t\t\t\t\t\t\t\t\t3=BalancedMemory,",
	" \t\t\t\t\t\t\t\t\t4=BalancedCore,\n\t\t\t\t\t\t\t\t\t5=BalancedCoreMemory",
	"  --setxgmipstaterange [SOCKET] [MIN<0,1>] [MAX<0,1>]\t\tSet xgmi pstate range (MAX <= MIN)",
	"  --setcpurailisofreqpolicy [SOCKET] [VAL<0,1>]\t\t\tSet CPU ISO frequency policy",
	"  --setdfcctrl [SOCKET] [VAL<0,1>]\t\t\t\tEnable or disable DF c-state"
};

static char* const feat_ver5_set[] = {
	"  --setpcielinkratecontrol [SOCKET] [CTL<0-2>]\t\t\tSet rate control for pcie link"
	" for a given socket",
	"  --setdfpstaterange [SOCKET] [MIN<0-2>] [MAX<0-2>]\t\tSet df pstate range"
	" for a given socket (MAX <= MIN)",
	"  --setgmi3linkwidth [SOCKET] [MIN<0-2>] [MAX<0-2>]\t\tSet gmi3 link width"
	" for a given socket (MAX >= MIN)",
};

static char* const feat_ver5_F1A_M50_5F_set[] = {
	"  --setdfpstaterange [SOCKET] [MIN<0-2>] [MAX<0-2>]\t\tSet df pstate range"
	" for a given socket (MAX <= MIN)",
};

static char* const feat_ver6_get[] = {
	"  --showcclkfreqlimit [CORE]\t\t\t\t\tShow current clock frequency limit(MHz) for a given core",
	"  --showsvipower \t\t\t\t\t\tShow svi based power telemetry of all rails for all sockets",
	"  --showxgmibw [SOCKET] [LINK<G0-G7>] [BW<AGG_BW,RD_BW,WR_BW>]\n\t\t\t\t\t\t\t\tShow xGMI bandwidth for a given socket,"
	" linkname and bwtype",
	"  --showiobw [SOCKET] [LINK<P2,P3,G0-G7>]\t\t\tShow IO aggregate bandwidth for a given socket and"
	" linkname",
	"  --showlclkdpmlevel [SOCKET] [NBIOID<0-3>]\t\t\tShow lclk dpm level for a given nbio"
	" in a given socket",
	"  --showsockclkfreqlimit [SOCKET]\t\t\t\tShow current clock frequency limit(MHz) for a given socket",
	"  --showmetrictablever\t\t\t\t\t\tShow Metrics Table Version",
	"  --showmetrictable [SOCKET]\t\t\t\t\tShow Metrics Table",
};

static char* const feat_ver6_set[] = {
	"Set Option<s>:",
	"  --setpowerlimit [SOCKET] [POWER]\t\t\t\tSet power limit"
	" for a given socket (mWatts)",
	"  --setcorebl [CORE] [BOOSTLIMIT]\t\t\t\tSet BoostLimit for a given core (MHz)",
	"  --setsockbl [SOCKET] [BOOSTLIMIT]\t\t\t\tSet BoostLimit for a given Socket (MHz)",
	"  --setxgmiwidth [SOCKET] [MIN<0-2>] [MAX<0-2>]\t\t\tSet xgmi link width"
	" in a multi socket system (MAX >= MIN)",
	"  --setlclkdpmlevel [SOCKET] [NBIOID<0-3>] [MIN<0-2>] [MAX<0-2>]Set lclk dpm level"
	" for a given nbio in a given socket (MAX >= MIN)",
};

static char* const feat_ver7_F1A_M50_5F_get[] = {
	"  --getapbstatus [SOCKET]\t\t\t\t\tGet APB status and Data Fabric pstate(if APBDisabled)",
	"  --getxgmiwidth [SOCKET]\t\t\t\t\tGet xgmi link width",
	"  --getdfpstaterange [SOCKET]\t\t\t\t\tGet df pstate range"
	" for a given socket",
	"  --getxgmipstaterange [SOCKET]\t\t\t\t\tGet xgmi pstate range for a given socket",
	"  --getccdpower [CORE]\t\t\t\t\t\tGet CCD power for a given core",
	"  --gettdelta [SOCKET]\t\t\t\t\t\tGet thermal solution behaviour for a given socket",
	"  --getsvi3vrtemp [SOCKET] [TYPE] [RAIL_INDEX(if TYPE=1)]\tGet svi3 vr controller temperature(TYPE:0->HottestRail,1->IndividualRail)\n\t\t\t\t\t\t\t\t(RAIL_INDEX:0->VDDCR_CPU0,1->VDDCR_CPU1,2->VDDCR_SOC,3->VDDIO,4->VDDIO_MEM_S3)",
	"  --getdimmsbdata [SOCKET] [DIMM_ADDR] [LID] [OFFSET] [REGSPACE]\n\t\t\t\t\t\t\t\tGet DIMM SB register data\n\t\t\t\t\t\t\t\t(LID:0x2->TS0,0x6->TS1,0x9->PMIC0,0xA->SPDHub)(REGSPACE:0->Volatile,1->NVM)",
};

static char* const feat_pc6_cc6_enabledcommands_floorlimit_get[] = {
	"  --getpc6enable [SOCKET]\t\t\t\t\tGet the PC6 Enable Control",
	"  --getcc6enable [SOCKET]\t\t\t\t\tGet the CC6 Enable Control",
	"  --getenabledcommands [SOCKET]\t\t\t\t\tGet the HSMP Enabled Commands",
	"  --getcorefl [CORE]\t\t\t\t\t\tGet FloorLimit for a given CPU/LogicalCore (MHz)",
	"  --getcoreefffl [CORE]\t\t\t\t\t\tGet EffectiveFloorLimit for a given CPU/LogicalCore (MHz)",
	"  --getsdpslimit [SOCKET]\t\t\t\t\tGet the SDPS Limit for a given socket (Watts)",
	"  --showmetrictablever\t\t\t\t\t\tShow Metric Table Version",
	"  --showmetrictable [SOCKET]\t\t\t\t\tShow Metric Table",
	"  --rawmetric\t\t\t\t\t\t\tShow as Raw MetricData",
	"  --metricdelay [METRIC_COLLECTION_DURATION] <TIME_RANGE<ms,s,m,h,d>>\n\t\t\t\t\t\t\t\tStart Metric Collection, then go for MetricDelay before gathering MetricData",
	"  --startmetric [START_METRIC_FILE_NAME]\t\t\tSet the StartMetric file name, to Start Metric Collection",
	"  --stopmetric  [STOP_METRIC_FILE_NAME]\t\t\t\tSet the StopMetric file name, to Stop Metric Collection",
};

static char* const feat_pc6_cc6_floorlimit_set[] = {
	"  --setdimmsbdata [SOCKET] [DIMM_ADDR] [LID] [OFFSET] [REGSPACE] [REGDATA]\n\t\t\t\t\t\t\t\tSet DIMM SB register data\n\t\t\t\t\t\t\t\t(LID:0x2->TS0,0x6->TS1,0x9->PMIC0,0xA->SPDHub)(REGSPACE:0->Volatile,1->NVM)",
    "  --setpc6enable [SOCKET] [val<0,1>]\t\t\t\tSet the PC6 Enable Control",
    "  --setcc6enable [SOCKET] [val<0,1>]\t\t\t\tSet the CC6 Enable Control",
	"  --setcorefl [CORE] [FLOORLIMIT]\t\t\t\tSet FloorLimit for a given core (MHz)",
	"  --setsockfl [SOCKET] [FLOORLIMIT]\t\t\t\tSet FloorLimit for a given Socket (MHz)",
	"  --setmsrcorefl [CORE] [FLOORLIMIT]\t\t\t\tSet FloorLimit for a given core (MHz) through MSR",
	"  --setmsrsockfl [SOCKET] [FLOORLIMIT]\t\t\t\tSet FloorLimit for a given Socket (MHz) through MSR",
	"  --setsdpslimit [SOCKET] [SDPSLIMIT]\t\t\t\tSet SDPS Limit for a given Socket (mWatts)",
};

static char* const blankline[] = {""};

static char **features = NULL;

static void show_usage(char *exe_name)
{
	int i = 0;

	printf("Usage: %s [Option]... <INPUT>...\n", exe_name);
	printf("Help : %s --help\n\n", exe_name);
	if(features != NULL)
	{
		while (features[i]) {
			printf("%s\n", features[i]);
			i++;
		}
	}
}

/*
 * returns 0 if the given string is a number, else 1
 */
static int is_string_number(char *str)
{
	int i;
	for (i = 0; str[i] != '\0'; i++) {
		if (i == 0 && str[i] == '-' && str[i + 1] != '\0') {
			continue;
		}
		if ((str[i] < '0') || (str[i] > '9')) {
			return 1;
		}
	}
	return 0;
}

/* copy the common and energy feature into features array */
static void add_comm_and_energy_feat(void)
{
	int offset = 0;

	memcpy(features, feat_comm, (ARRAY_SIZE(feat_comm) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_comm);
	memcpy(features + offset, feat_energy, (ARRAY_SIZE(feat_energy) * sizeof(char *)));
}

/* copy the hsmp proto ver2 specific features into features array */
static void add_hsmp_ver2_feat(void)
{
	int offset = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy);

	memcpy(features + offset, feat_ver2_get, (ARRAY_SIZE(feat_ver2_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_get);
	memcpy(features + offset, blankline, sizeof(char *));
	offset += 1;
	memcpy(features + offset, feat_ver2_set, (ARRAY_SIZE(feat_ver2_set) * sizeof(char *)));

	/* proto version 2 is the baseline for HSMP fetaures, so there is no addon */
	sys_info.show_addon_cpu_metrics = no_addon_cpu_metrics;
	sys_info.show_addon_socket_metrics = no_addon_socket_metrics;
	sys_info.show_addon_clock_metrics = no_addon_clock_metrics;
}

/* copy the hsmp proto ver4 specific features into features array */
static void add_hsmp_ver4_feat(void)
{
	int offset = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy);

	memcpy(features + offset, feat_ver2_get, (ARRAY_SIZE(feat_ver2_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_get);
	memcpy(features + offset, feat_ver3, (ARRAY_SIZE(feat_ver3) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver3);
	memcpy(features + offset, feat_ver4, (ARRAY_SIZE(feat_ver4) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver4);
	memcpy(features + offset, blankline, sizeof(char *));
	offset += 1;
	memcpy(features + offset, feat_ver2_set, (ARRAY_SIZE(feat_ver2_set) * sizeof(char *)));

	/* proto version 4 has extra socket metrics, cpu metrics are same as ver2 */
	sys_info.show_addon_cpu_metrics = no_addon_cpu_metrics;
	sys_info.show_addon_socket_metrics = socket_ver4_metrics;
	sys_info.show_addon_clock_metrics = no_addon_clock_metrics;
}

/* copy the hsmp proto ver5 specific features into features array */
static void add_hsmp_ver5_feat(void)
{
	int offset = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy);

	memcpy(features + offset, feat_ver2_get, (ARRAY_SIZE(feat_ver2_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_get);
	memcpy(features + offset, feat_ver3, (ARRAY_SIZE(feat_ver3) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver3);
	memcpy(features + offset, feat_ver5_get, (ARRAY_SIZE(feat_ver5_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver5_get);
	if (sys_info.family == 0x1A && sys_info.model <= 0x1F) {
		memcpy(features + offset, feat_ver5_F1A_M00_1F_get,
		       (ARRAY_SIZE(feat_ver5_F1A_M00_1F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M00_1F_get);
	} else {
		memcpy(features + offset, feat_ver5_F19_M00_0F_get,
		       (ARRAY_SIZE(feat_ver5_F19_M00_0F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F19_M00_0F_get);
	}
	memcpy(features + offset, blankline, sizeof(char *));
	offset += 1;
	memcpy(features + offset, feat_ver2_set, (ARRAY_SIZE(feat_ver2_set) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_set);
	memcpy(features + offset, feat_ver5_set, (ARRAY_SIZE(feat_ver5_set) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver5_set);
	if (sys_info.family == 0x1A && sys_info.model <= 0x1F) {
		memcpy(features + offset, feat_ver5_F1A_M00_1F_set,
		       (ARRAY_SIZE(feat_ver5_F1A_M00_1F_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M00_1F_set);
	} else {
		memcpy(features + offset, feat_ver5_F19_M00_0F_set,
		       (ARRAY_SIZE(feat_ver5_F19_M00_0F_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F19_M00_0F_set);
	}

	/* proto version 5 has extra socket metrics as well as extra cpu metrics */
	sys_info.show_addon_cpu_metrics = cpu_ver5_metrics;
	sys_info.show_addon_socket_metrics = socket_ver5_metrics;
	sys_info.show_addon_clock_metrics = clock_ver5_metrics;
}

/* copy the hsmp proto ver6 specific features into features array */
static void add_hsmp_ver6_feat(void)
{
	int offset = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy);

	memcpy(features + offset, feat_ver2_get, (ARRAY_SIZE(feat_ver2_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_get);
	memcpy(features + offset, feat_ver6_get, (ARRAY_SIZE(feat_ver6_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver6_get);
	memcpy(features + offset, blankline, sizeof(char *));
	offset += 1;
	memcpy(features + offset, feat_ver6_set, (ARRAY_SIZE(feat_ver6_set) * sizeof(char *)));

	/* version 6 cpu metrics is same as version 5 */
	sys_info.show_addon_cpu_metrics = cpu_ver5_metrics;
	sys_info.show_addon_socket_metrics = socket_ver6_metrics;
	sys_info.show_addon_clock_metrics = clock_ver5_metrics;
}

static void add_hsmp_ver7_feat(void)
{
	int offset = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy);

	/* copy all the "get" messages */
	memcpy(features + offset, feat_ver2_get, (ARRAY_SIZE(feat_ver2_get) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_get);
	memcpy(features + offset, feat_ver3, (ARRAY_SIZE(feat_ver3) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver3);
	if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F)))
	{
		memcpy(features + offset, feat_ver5_F1A_M50_5F_get, (ARRAY_SIZE(feat_ver5_F1A_M50_5F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M50_5F_get);

		memcpy(features + offset, feat_ver5_xgmibw_F1A_M50_5F_get,(ARRAY_SIZE(feat_ver5_xgmibw_F1A_M50_5F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_xgmibw_F1A_M50_5F_get);
	} else {
		memcpy(features + offset, feat_ver5_get, (ARRAY_SIZE(feat_ver5_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_get);

		memcpy(features + offset, feat_ver5_F1A_M00_1F_get,(ARRAY_SIZE(feat_ver5_F1A_M00_1F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M00_1F_get);
	}

	if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) {
		memcpy(features + offset, feat_ver7_F1A_M50_5F_get,
		       (ARRAY_SIZE(feat_ver7_F1A_M50_5F_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver7_F1A_M50_5F_get);
		memcpy(features + offset, feat_pc6_cc6_enabledcommands_floorlimit_get,
		       (ARRAY_SIZE(feat_pc6_cc6_enabledcommands_floorlimit_get) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_pc6_cc6_enabledcommands_floorlimit_get);
	}

	memcpy(features + offset, blankline, sizeof(char *));
	offset += 1;

	/* copy all the "set" messages */
	memcpy(features + offset, feat_ver2_set, (ARRAY_SIZE(feat_ver2_set) * sizeof(char *)));
	offset += ARRAY_SIZE(feat_ver2_set);
	if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) {
		memcpy(features + offset, feat_ver5_F1A_M50_5F_set, (ARRAY_SIZE(feat_ver5_F1A_M50_5F_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M50_5F_set);

		memcpy(features + offset, feat_ver7_F1A_M50_5F_set,
	       (ARRAY_SIZE(feat_ver7_F1A_M50_5F_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver7_F1A_M50_5F_set);
	} else {
		memcpy(features + offset, feat_ver5_set, (ARRAY_SIZE(feat_ver5_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_set);

		memcpy(features + offset, feat_ver5_F1A_M00_1F_set,
	       (ARRAY_SIZE(feat_ver5_F1A_M00_1F_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_ver5_F1A_M00_1F_set);
	}

	if((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) {
		memcpy(features + offset, feat_pc6_cc6_floorlimit_set,
		       (ARRAY_SIZE(feat_pc6_cc6_floorlimit_set) * sizeof(char *)));
		offset += ARRAY_SIZE(feat_pc6_cc6_floorlimit_set);
	}

	/* proto version 7 metrics are same as version 5 */
	sys_info.show_addon_cpu_metrics = cpu_ver5_metrics;
	sys_info.show_addon_socket_metrics = socket_ver5_metrics;
	sys_info.show_addon_clock_metrics = clock_ver5_metrics;
}

static esmi_status_t init_proto_version_func_pointers()
{
	uint32_t proto_ver;
	esmi_status_t ret;
	int size;

	ret = esmi_hsmp_proto_ver_get(&proto_ver);
	if (ret) {
		printf(RED "Error in initialising HSMP version sepcific info, "
			"Only energy data can be obtained...\nErr[%d]: %s\n\n" RESET,
			ret, esmi_get_err_msg(ret));
		size = ARRAY_SIZE(feat_energy) + ARRAY_SIZE(feat_comm);
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		/* Indicate the array end with NULL pointer */
		features[size] = NULL;
		return ESMI_SUCCESS;
	}

	switch (proto_ver) {
	case 2:
		size = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_energy) +
		       ARRAY_SIZE(feat_ver2_get) + ARRAY_SIZE(feat_ver2_set) +
		       ARRAY_SIZE(blankline);
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		add_hsmp_ver2_feat();
		break;
	case 4:
		size = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_ver2_get) +
		       ARRAY_SIZE(feat_ver2_set) + ARRAY_SIZE(feat_ver4) +
		       ARRAY_SIZE(feat_energy) + ARRAY_SIZE(feat_ver3) + ARRAY_SIZE(blankline);
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		add_hsmp_ver4_feat();
		break;
	case 5:
		size = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_ver2_get) +
		       ARRAY_SIZE(feat_ver2_set) + ARRAY_SIZE(feat_ver5_get) +
		       ARRAY_SIZE(feat_ver5_set) + ARRAY_SIZE(feat_ver3) +
		       ARRAY_SIZE(feat_energy) + ARRAY_SIZE(blankline);
		if (sys_info.family == 0x1A && sys_info.model <= 0x1f) {
			size += ARRAY_SIZE(feat_ver5_F1A_M00_1F_get);
			size += ARRAY_SIZE(feat_ver5_F1A_M00_1F_set);
		} else {
			size += ARRAY_SIZE(feat_ver5_F19_M00_0F_get);
			size += ARRAY_SIZE(feat_ver5_F19_M00_0F_set);
		}
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		add_hsmp_ver5_feat();
		break;
	case 6:
		size = ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_ver2_get) +
		       ARRAY_SIZE(feat_energy) + ARRAY_SIZE(feat_ver6_get) +
		       ARRAY_SIZE(feat_ver6_set) + ARRAY_SIZE(blankline);
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		add_hsmp_ver6_feat();
		break;
	case 7:
	default:
		if (proto_ver > 7) {
			printf(MAG "This version of the library does not fully support your platform.\n");
			printf(MAG "defaulting to highest supported platform feature set, may support common features across platform.\n");
			printf(MAG "Please upgrade the library.\n\n" RESET);
		}
		size = 	ARRAY_SIZE(feat_comm) + ARRAY_SIZE(feat_ver2_get) + ARRAY_SIZE(feat_ver2_set);

		if((0x1A == sys_info.family) && (sys_info.model >= 0x50) && (sys_info.model <= 0x5F))
			size += ARRAY_SIZE(feat_ver5_F1A_M50_5F_get);
		else
			size += ARRAY_SIZE(feat_ver5_get);

		size += ARRAY_SIZE(feat_ver3) + ARRAY_SIZE(blankline) + ARRAY_SIZE(feat_energy);

		if((0x1A == sys_info.family) && (sys_info.model >= 0x50) && (sys_info.model <= 0x5F))
			size += ARRAY_SIZE(feat_ver7_F1A_M50_5F_set) + ARRAY_SIZE(feat_ver5_xgmibw_F1A_M50_5F_get);
		else
			size += ARRAY_SIZE(feat_ver5_F1A_M00_1F_set) + ARRAY_SIZE(feat_ver5_F1A_M00_1F_get);

		if((0x1A == sys_info.family) && (sys_info.model >= 0x50) && (sys_info.model <= 0x5F))
		{
			size += ARRAY_SIZE(feat_ver5_F1A_M50_5F_set) +
				ARRAY_SIZE(feat_ver7_F1A_M50_5F_get) +
				ARRAY_SIZE(feat_pc6_cc6_enabledcommands_floorlimit_get) +
				ARRAY_SIZE(feat_pc6_cc6_floorlimit_set);
		}
		else
		{
			size += ARRAY_SIZE(feat_ver5_set);
		}
		features = malloc((size + 1) * sizeof(char *));
		if (!features)
			return ESMI_NO_MEMORY;
		add_comm_and_energy_feat();
		add_hsmp_ver7_feat();
		break;
	}

	/* Indicate the array end with NULL pointer */
	features[size] = NULL;

	return ESMI_SUCCESS;
}

static void print_esmi_version()
{

	printf("-----------------------------------------------------------\n");
	printf("| E-smi library version  |  %d.%d.%d build: %-10s \t |\n",
	       e_smi_VERSION_MAJOR, e_smi_VERSION_MINOR, e_smi_VERSION_PATCH, e_smi_VERSION_BUILD);
	printf("-----------------------------------------------------------\n");
}

static int parse_flag_based_options(char **argv, int val)
{
	uint32_t sock_id = 0;
	uint32_t core_id = 0;
	uint32_t limit_received = 0;
	int ret = 0;
	uint8_t enable;
	uint8_t type;
	uint8_t rail_index;
	uint32_t core_ind;

	uint64_t reg_space;
	uint64_t write_data;
	uint64_t reg_offset;
	uint64_t dimm_addr;
	uint64_t lid;
	char *end = 0;
	union dimm_power_inarg power_inarg;
	switch(val)
		{
		case 1:
			sock_id = atoi(optarg);
			/* Get DF Pstate to user specified value */
			ret = epyc_get_df_pstate(sock_id);
			break;
		case 2:
			/* Get xgmi link width */
			sock_id = atoi(optarg);
			ret = epyc_get_xgmi_width(sock_id);
			break;
		case 4:
			/* Get df pstate range */
			sock_id = atoi(optarg);
			ret = epyc_get_df_pstate_range(sock_id);
			break;
		case 5:
			sock_id = atoi(optarg);
			ret = epyc_get_xgmi_pstate_range(sock_id);
			break;
		case 6:
			/* Set PC6 Enable */
			sock_id = atoi(optarg);
			enable = atoi(argv[optind++]);
			ret = epyc_set_pc6_enable(sock_id, enable);
			break;
		case 7:
			/* Get PC6 Enable */
			sock_id = atoi(optarg);
			ret = epyc_get_pc6_enable(sock_id);
			break;
		case 8:
			/* Set CC6 Enable */
			sock_id = atoi(optarg);
			enable = atoi(argv[optind++]);
			ret = epyc_set_cc6_enable(sock_id, enable);
			break;
		case 9:
			/* Get CC6 Enable */
			sock_id = atoi(optarg);
			ret = epyc_get_cc6_enable(sock_id);
			break;
		case 20:
			/* Get CCD power */
			core_ind = atoi(optarg);
			ret = epyc_get_ccd_power(core_ind);
			break;
		case 21:
			/* Get tdelta */
			sock_id = atoi(optarg);
			ret = epyc_get_tdelta(sock_id);
			break;
		case 22:
			/* Get read dimm reg */
			sock_id = atoi(optarg);
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				dimm_addr = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				dimm_addr = atol(argv[optind++]);//Adding Extra Arguments
			}
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				lid = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				lid = atol(argv[optind++]);//Adding Extra Arguments
			}
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				reg_offset = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				reg_offset = atol(argv[optind++]);//Adding Extra Arguments
			}
			reg_space =  atoi(argv[optind++]);
			ret = epyc_read_dimm_sb_reg(sock_id, dimm_addr, lid, reg_offset, reg_space);
			break;
		case 31:
			/* Set read dimm reg */
			sock_id = atoi(optarg);
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				dimm_addr = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				dimm_addr = atol(argv[optind++]);//Adding Extra Arguments
			}
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				lid = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				lid = atol(argv[optind++]);//Adding Extra Arguments
			}
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				reg_offset = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				reg_offset = atol(argv[optind++]);//Adding Extra Arguments
			}
			reg_space =  atoi(argv[optind++]);
			if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
			{
				write_data = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
			} else {
				write_data = atol(argv[optind++]);//Adding Extra Arguments
			}
			ret = epyc_write_dimm_sb_reg(sock_id, dimm_addr, lid, reg_offset, reg_space, write_data);
			break;
		case 23:
			/* Get svi3 vr temp */
			sock_id = atoi(optarg);
			type = atoi(argv[optind++]);
			if (type)
				rail_index =  atoi(argv[optind++]);
			ret = epyc_get_svi3_vr_temp(sock_id, type, rail_index);
			break;
		case 24:
			/* Get Enabled Commands */
			sock_id = atoi(optarg);
			ret = epyc_get_enabled_commands(sock_id);
			break;
		case 25:
			/* Get the FloorLimit for a given core index */
			core_id = atoi(optarg);
			ret = epyc_get_floorlimit(core_id, FLOOR_FREQUENCY);
			break;
		case 26:
			/* Get the Effective FloorLimit for a given core index */
			core_id = atoi(optarg);
			ret = epyc_get_floorlimit(core_id, EFF_FLOOR_FREQUENCY);
			break;
		case 27:
			/* Set the floorlimit value for a given core */
			core_id = atoi(optarg);
			limit_received = atoi(argv[optind++]);
			ret = epyc_set_floorlimit(core_id, limit_received);
			break;
		case 28:
			/* Set the floorlimit value for a given socket */
			sock_id = atoi(optarg);
			limit_received = atoi(argv[optind++]);
			ret = epyc_set_socket_floorlimit(sock_id, limit_received);
			break;
		case 29:
			/* Set the floorlimit value for a given core */
			core_id = atoi(optarg);
			limit_received = atoi(argv[optind++]);
			ret = epyc_set_msr_floorlimit(core_id, limit_received);
			break;
		case 30:
			/* Set the floorlimit value for a given socket */
			sock_id = atoi(optarg);
			limit_received = atoi(argv[optind++]);
			ret = epyc_set_msr_socket_floorlimit(sock_id, limit_received);
			break;
		case 32:
			/* Get SDPS Limit for a given socket */
			sock_id = atoi(optarg);
			ret = epyc_get_sdps_limit(sock_id);
			break;
		case 33:
			/* Set the SDPS Limit for a given socket */
			sock_id = atoi(optarg);
			limit_received = atoi(argv[optind++]);
			ret = epyc_set_sdps_limit(sock_id, limit_received);
			break;
		case 38:
			/* Get MAX DIMM temperature */
			sock_id = atoi(optarg);
			union dimm_temp_inarg temp_inarg;
			temp_inarg.info.dimm_flag = MAX_DIMM_TEMP;
			ret = epyc_get_dimm_thermal_data(sock_id, temp_inarg);
			break;
		case 39:
			/* Get MAX DIMM power consumption */
			sock_id = atoi(optarg);
			power_inarg.reg_value = 0;
			power_inarg.info.dimm_flag = MAX_DIMM_POWER;
			ret  = epyc_get_dimm_power_data(sock_id, power_inarg);
			break;
		case 40:
			/* Get TOTAL DIMM power consumption */
			sock_id = atoi(optarg);

			power_inarg.reg_value = 0;
			power_inarg.info.dimm_flag = TOTAL_DIMM_POWER;
			ret  = epyc_get_dimm_power_data(sock_id, power_inarg);
			break;
		default:
			break;
		}

	return ret;
}

static bool check_flag_option_for_1arg(int flag)
{
	switch(flag) {
	case 1 ... 9:
	case 20 ... 21:
	case 23 ... 30:
	case 32 ... 33:
	case 38 ... 40:
		return true;
	default:
		return false;
	}
}

static bool check_flag_option_for_2arg(int flag)
{
	switch(flag)
	{
		case 6:
		case 8:
		case 23:
		case 27 ... 30:
		case 33:
			return true;
		default:
			return false;
	}
}

/**
Parse command line parameters and set data for program.
@param argc number of command line parameters
@param argv list of command line parameters
*/
static int parsesmi_args(int argc,char **argv)
{
	int ret = ESMI_INVALID_INPUT;
	int i;
	int opt = 0; /* option character */
	uint32_t core_id = 0, sock_id = 0;
	uint32_t power = 0, boostlimit = 0;
	int32_t pstate;
	static char *args[ARGS_MAX];
	char sudostr[] = "sudo";
	uint8_t min, max;
	uint8_t nbio_id;
	char *end;
	char *link_name;
	char *bw_type;
	uint8_t ctrl, mode, value;
	uint64_t input_data;
	uint8_t type;
	uint32_t pwreffmode_util = 0, pwreffmode_pptlimit = 0;
	uint64_t temp_var = 0, temp_var1 = 0;

	//Specifying the expected options
	static struct option long_options[] = {
		{"help",                no_argument,		0,	'h'},
		{"showall",             no_argument,		0,	'A'},
		{"testmailbox",         required_argument,	0,	'N'},
		{"showcoreenergy",      required_argument,	0,	'e'},
		{"showsockenergy",	no_argument,		0,	's'},
		{"showsockpower",	no_argument,		0,	'p'},
		{"showsmufwver",	no_argument,		0,	'f'},
		{"showhsmpdriverver",	no_argument,		0,	'o'},
		{"showcorebl",		required_argument,	0,	'L'},
		{"setpowerlimit",	required_argument,	0,	'C'},
		{"setcorebl",		required_argument,	0,	'a'},
		{"setsockbl",		required_argument,	0,	'b'},
		{"showsockc0resi",	required_argument,	0,	'r'},
		{"showddrbw",		no_argument,		0,	'd'},
		{"showsockettemp",	no_argument,		0,      't'},
		{"showhsmpprotover",	no_argument,		0,	'v'},
		{"showprochotstatus",	no_argument,		0,	'x'},
		{"apbenable",		required_argument,	0,	'y'},
		{"apbdisable",		required_argument,	0,	'u'},
		{"showclocks",		no_argument,		0,	'z'},
		{"setxgmiwidth",	required_argument,	0,	'w'},
		{"setlclkdpmlevel",	required_argument,	0,	'l'},
		{"showdimmthermal",		required_argument,	0,	'H'},
		{"showdimmpower",		required_argument,	0,	'g'},
		{"showdimmtemprange",		required_argument,	0,	'T'},
		{"showcclkfreqlimit",		required_argument,	0,	'q'},
		{"showsvipower",		no_argument,		0,	'm'},
		{"showiobw",			required_argument,	0,	'B'},
		{"showxgmibw",			required_argument,	0,	'i'},
		{"setpcielinkratecontrol",	required_argument,	0,	'j'},
		{"setpowerefficiencymode",	required_argument,	0,	'k'},
		{"setdfpstaterange",		required_argument,	0,	'X'},
		{"setgmi3linkwidth",		required_argument,	0,	'n'},
		{"showlclkdpmlevel",		required_argument,	0,	'Y'},
		{"showsockclkfreqlimit",	required_argument,	0,	'Q'},
		{"showmetrictablever",		no_argument,		0,	'D'},
		{"showmetrictable",		required_argument,	0,	'J'},
		{"version",			no_argument,		0,	'V'},
		{"writemsrallowlist",		no_argument,		0,	'W'},
		{"showcurrpwrefficiencymode", 	required_argument, 	0, 	'O'},
		{"showcpurailisofreqpolicy", 	required_argument, 	0, 	'K'},
		{"showdfcstatectrl", 		required_argument, 	0, 	'M'},
		{"setxgmipstaterange", 		required_argument, 	0, 	'E'},
		{"setcpurailisofreqpolicy", 	required_argument, 	0, 	'F'},
		{"setdfcctrl", 			required_argument, 	0, 	'P'},
		{"getapbstatus",		required_argument,	&flag,	1},
		{"getxgmiwidth",		required_argument,	&flag,	2},
		{"getdfpstaterange",		required_argument,	&flag,	4},
		{"getxgmipstaterange", 		required_argument, 	&flag, 	5},
		{"setpc6enable", 		required_argument, 	&flag, 	6},
		{"getpc6enable", 		required_argument, 	&flag, 	7},
		{"setcc6enable", 		required_argument, 	&flag, 	8},
		{"getcc6enable", 		required_argument, 	&flag, 	9},
		{"json", 			no_argument,	 	&flag, 	12},
		{"csv", 			no_argument,	 	&flag, 	13},
		{"initialdelay", 		required_argument, 	&flag, 	14},
		{"loopdelay", 			required_argument, 	&flag, 	15},
		{"loopcount", 			required_argument, 	&flag, 	16},
		{"printonconsole", 		required_argument, 	&flag, 	17},
		{"log", 			required_argument, 	&flag, 	18},
		{"stoploop", 			required_argument, 	&flag, 	19},
		{"getccdpower",			required_argument, 	&flag, 	20},
		{"gettdelta",			required_argument, 	&flag, 	21},
		{"getdimmsbdata",		required_argument, 	&flag, 	22},
		{"getsvi3vrtemp",		required_argument, 	&flag, 	23},
		{"getenabledcommands",	required_argument,	&flag,	24},
		{"getcorefl",			required_argument,	&flag,	25},
		{"getcoreefffl",		required_argument,	&flag,	26},
		{"setcorefl",			required_argument,	&flag,	27},
		{"setsockfl",			required_argument,	&flag,	28},
		{"setmsrcorefl",		required_argument,	&flag,	29},
		{"setmsrsockfl",		required_argument,	&flag,	30},
		{"setdimmsbdata",		required_argument, 	&flag, 	31},
		{"getsdpslimit",		required_argument,	&flag,	32},
		{"setsdpslimit",		required_argument,	&flag,	33},
		{"metricdelay",			required_argument, 	&flag, 	34},
		{"rawmetric",			no_argument, 		&flag, 	35},
		{"startmetric",			required_argument,	&flag,	36},
		{"stopmetric",			required_argument,	&flag,	37},
		{"showmaxdimmthermal",	required_argument,	&flag,	38},
		{"showmaxdimmpower",	required_argument,	&flag,	39},
		{"showtotaldimmpower",	required_argument,	&flag,	40},
		{0,				0,			0,	0},
	};

	int long_index = 0;
	char *helperstring = "+hAV";

	optind = 0;
	char* err_string = NULL;
	int ret_on_err = ESMI_INVALID_INPUT;
	bool show_usage_on_err = false;

	/* smi monitor objects initialization - Start*/
	ret = esmi_init();
	if(ret != ESMI_SUCCESS) {
		printf(RED "\tESMI Not initialized, drivers not found.\n"
		       "\tErr[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	ret = cache_system_info();
	if(ret != ESMI_SUCCESS) {
		printf(RED "\tError in reading system info.\n"
		       "\tErr[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}

	ret = init_proto_version_func_pointers();
	if (ret != ESMI_SUCCESS) {
		printf(RED "\tError in allocating memory \n"
		       "\tErr[%d]: %s\n" RESET, ret, esmi_get_err_msg(ret));
		return ret;
	}
	/* smi monitor objects initialization - End*/

	while ((opt = getopt_long(argc, argv, helperstring,
			long_options, &long_index)) != -1)
	{
		char temp_string[300];
		if (opt == ':')
		{
			/* missing option argument */
			sprintf(temp_string, "%s: option '-%c' requires an argument.\n\n", argv[0], opt);
			append_string(&err_string, temp_string);
			break;
		}
		else if (opt == '?')
		{
			if (isprint(opt)) {
				sprintf(temp_string, "Try `%s --help' for more information.\n", argv[0]);
			} else {
				sprintf(temp_string, "Unknown option character `\\x%x'.\n", opt);
			}
			append_string(&err_string, temp_string);
			break;
		}

		if (opt == 'e' || opt == 'y' || opt == 'L' || opt == 'r' || opt == 'q' || opt == 'Q' || opt == 'J' ||
			opt == 'O' || opt == 'M' || opt == 'K' ||
			opt == 'N' || opt == 'C' || opt == 'a' || opt == 'b' || opt == 'u' || opt == 'H' || opt == 'g' || opt == 'T' ||
			opt == 'B' || opt == 'j' || opt == 'k' || opt == 'Y' || opt == 'F' || opt == 'P' ||
			((opt == 0) && (check_flag_option_for_1arg(*long_options[long_index].flag))) ||
			opt == 'l'
		/*opt == 'i' *//*|| opt == 'n' || opt == 'X' || opt == 'w' || opt == 'E'*/)
		{
			if (is_string_number(optarg))
			{
				sprintf(temp_string, "Option '--%s' require a valid numeric value as an argument\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			if (*optarg == '-') {
				sprintf(temp_string, "Option '--%s', Negative values are not accepted\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				break;
			}

			if (opt == 'J') {
				sock_id = atoi(argv[optind-1]);
				show_metric_table[sock_id] = true;
			}
		}
		if (opt == 'N' || opt == 'C' || opt == 'a' || opt == 'b' || opt == 'u' || opt == 'H' || opt == 'g' || opt == 'T' ||
			opt == 'j' || opt == 'k' || opt == 'Y' || opt == 'F' || opt == 'P' ||
			/*opt == 'i'*//*opt == 'n' || opt == 'X' || opt == 'w' || opt == 'E' ||*/
			((opt == 0) && (check_flag_option_for_2arg(*long_options[long_index].flag))))
		{
			// make sure optind is valid  ... or another option
			if (optind >= argc) {
				sprintf(temp_string, "\nOption '--%s' require TWO arguments <index>  <set_value>\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			if (opt == 'N' || opt == 'H' || opt == 'g' || opt == 'T')
			{
				if (*argv[optind] == '-') {
					sprintf(temp_string, "\nOption '--%s' requires TWO arguments and value should be non negative\n\n", long_options[long_index].name);
					append_string(&err_string, temp_string);
					show_usage_on_err = true;
					break;
				}
				if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
				{
					input_data = strtoul(argv[optind++], &end, 16);
					if (*end) {
						sprintf(temp_string, "Option '--%s' requires 2nd argument as valid numeric value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				} else {
					if (is_string_number(argv[optind])) {
						sprintf(temp_string, "Option '--%s' requires 2nd argument as valid numeric value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
					input_data = atol(argv[optind++]);
				}
			} else {
				if (is_string_number(argv[optind])) {
					sprintf(temp_string, "Option '--%s' requires 2nd argument as valid numeric value\n\n", long_options[long_index].name);
					append_string(&err_string, temp_string);
					show_usage_on_err = true;
					break;
				}
				if (*argv[optind] == '-') {
					sprintf(temp_string, "Option '--%s', Negative values are not accepted\n\n", long_options[long_index].name);
					append_string(&err_string, temp_string);
					break;
				}

				if (opt == 'k')
				{
					temp_var =  atoi(argv[optind]);//Adding Extra Arguments
					if(temp_var > MAX_POWER_EFFICIENCY_MODE)
					{
						sprintf(temp_string, "Option '--%s' requires valid 2nd argument MODE\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
					mode = temp_var;
					if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
					{
						optind++;
						if (optind >= argc) {
							sprintf(temp_string, "Option '--%s' requires 3rd argument Util(%%)<0-100> when MODE = 4/5\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
						temp_var =  atoi(argv[optind]);//Adding Extra Arguments
						if (is_string_number(argv[optind]) || (*argv[optind] == '-') || (temp_var > MAX_POWER_EFFICIENCY_UTIL)) {
							sprintf(temp_string, "\nOption '--%s' requires 3rd arguments as valid numeric value Util(%%)<0-100> when MODE = 4/5 and should be non negative\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}

						optind++;
						if (optind >= argc) {
							sprintf(temp_string, "Option '--%s' requires 4th argument PptLimit(mW) when MODE = 4/5\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
						temp_var =  atoi(argv[optind]);//Adding Extra Arguments
						if (is_string_number(argv[optind]) || (*argv[optind] == '-') || (temp_var > MAX_POWER_EFFICIENCY_PPTLIMIT)) {
							sprintf(temp_string, "\nOption '--%s' requires 4th arguments as valid numeric value PptLimit(mW) when MODE = 4/5 and should be non negative\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
					}
				}
				else if ((opt == 0) && (*long_options[long_index].flag == 23))
				{
					type =  atoi(argv[optind]);//Adding Extra Arguments
					if (type) {
						optind++;
						if (type > MAX_SVI3_RAIL_SELECTION)
						{
							sprintf(temp_string, "Option '--%s' requires valid 2nd argument TYPE, TYPE:0->HottestRail,1->IndividualRail\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
						if (optind >= argc) {
							sprintf(temp_string, "Option '--%s' requires 3rd argument RAIL_INDEX when TYPE = 1(IndividualRail)\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
						if (is_string_number(argv[optind]) || *argv[optind] == '-') {
							sprintf(temp_string, "\nOption '--%s' requires THIRD arguments as valid numeric value and should be non negative\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
					}
				}
				else if ((opt == 0) && (*long_options[long_index].flag == 33))
				{
					temp_var =  atoi(argv[optind]);//Adding Extra Arguments
					if(temp_var > MAX_SDPSLIMIT)
					{
						sprintf(temp_string, "Option '--%s' requires valid 2nd argument SDPSLIMIT\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				}

				if (opt == 'C' || opt == 'a' || opt == 'b' || opt == 'u' ||
					opt == 'j' || opt == 'k' || opt == 'Y' || opt == 'F' || opt == 'P' ||
					((opt == 0) && (check_flag_option_for_2arg(*long_options[long_index].flag))))
				{
					optind++;//Adding Extra Arguments
				}
			}
		}
		if (opt == 'B')
		{
			if ((optind >= argc) || (*optarg == '-') || (*argv[optind] == '-')) {
				sprintf(temp_string, "\nOption '--%s' requires two valid arguments <arg1> <arg2>\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			if (is_string_number(optarg) || !is_string_number(argv[optind])) {
				sprintf(temp_string, "Option '--%s', Please provide valid link names.\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				break;
			}
			optind++;//Adding Extra Arguments
		}
		if (opt == 'l') {
			// make sure optind is valid  ... or another option
			if ((optind + 2) >= argc ) {
				sprintf(temp_string, "\nOption '--%s' requires FOUR arguments <socket> <nbioid> <min_value> <max_value>\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}

			if (is_string_number(argv[optind]) || is_string_number(argv[optind + 1]) || is_string_number(argv[optind + 2])) {
				sprintf(temp_string, "Option '--%s' requires 2nd, 3rd, 4th argument as valid numeric value\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			if (*argv[optind] == '-' || *argv[optind + 1] == '-' || *argv[optind + 2] == '-') {
				sprintf(temp_string, "Option '--%s', Negative values are not accepted\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				break;
			}
			optind++;//Adding Extra Arguments
			optind++;//Adding Extra Arguments
			optind++;//Adding Extra Arguments
		}
		if (opt == 'i')
		{
			if (((optind + 1) >= argc) || (*optarg == '-') || (*argv[optind + 1] == '-')) {
				sprintf(temp_string, "\nOption '--%s' requires three valid arguments <socket> <Link> <BW>\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			if (is_string_number(optarg) || !is_string_number(argv[optind]) || !is_string_number(argv[optind + 1])) {
				sprintf(temp_string, "Option '--%s', Please provide valid arguments <socket> <Link> <BW>.\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				break;
			}
			optind++;//Adding Extra Arguments
			optind++;//Adding Extra Arguments
		}
		if ((opt == 'n') || (opt == 'X') || (opt == 'w') || (opt == 'E')) {
			// make sure optind is valid  ... or another option
			if ((optind + 1) >= argc || *argv[optind] == '-' || *argv[optind + 1] == '-') {
				sprintf(temp_string, "\nOption '--%s' requires THREE arguments <socket> <min_value> <max_value>\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			temp_var =  atoi(argv[optind]);//Adding Extra Arguments
			temp_var1 =  atoi(argv[optind+1]);//Adding Extra Arguments
			if ((is_string_number(argv[optind]) || is_string_number(argv[optind + 1])) || (temp_var > MAX_RANGE_0xFF) || (temp_var1 > MAX_RANGE_0xFF)) {
				sprintf(temp_string, "Option '--%s' requires 2nd, 3rd, as valid numeric value\n\n", long_options[long_index].name);
				append_string(&err_string, temp_string);
				show_usage_on_err = true;
				break;
			}
			optind++;//Adding Extra Arguments
			optind++;//Adding Extra Arguments
		}

		if ((opt == 0) &&
			((*long_options[long_index].flag == 12) || (*long_options[long_index].flag == 13) || (*long_options[long_index].flag == 14) ||
			 (*long_options[long_index].flag == 15) || (*long_options[long_index].flag == 16) || (*long_options[long_index].flag == 17) ||
			 (*long_options[long_index].flag == 18) || (*long_options[long_index].flag == 19) || (*long_options[long_index].flag == 22) ||
			 (*long_options[long_index].flag == 31) || (*long_options[long_index].flag == 34) || (*long_options[long_index].flag == 35) ||
			 (*long_options[long_index].flag == 36) || (*long_options[long_index].flag == 37)))
		{
			if (*long_options[long_index].flag == 12) {
				if(print_results != DONT_PRINT_RESULTS)
					print_results = PRINT_RESULTS_AS_JSON;
			}
			else if (*long_options[long_index].flag == 13) {
				if(print_results != DONT_PRINT_RESULTS)
					print_results = PRINT_RESULTS_AS_CSV;
			}
			else if (*long_options[long_index].flag == 35) {
				metric_table_format = RAW_METRIC_TABLE_FORMAT;
			}
			else if ((*long_options[long_index].flag == 14) || (*long_options[long_index].flag == 15) || (*long_options[long_index].flag == 16) ||
					 (*long_options[long_index].flag == 17) || (*long_options[long_index].flag == 34))
			{
				if (is_string_number(optarg)) {
					sprintf(temp_string, "Option '--%s' require a valid numeric value as an argument\n\n", long_options[long_index].name);
					append_string(&err_string, temp_string);
					break;
				}
				if(*long_options[long_index].flag == 16)
				{
					if (*optarg == '-') {
						int arg_passed = atoi(optarg);
						if (arg_passed != -1) {
							sprintf(temp_string, "Option '--%s', Negative values are not accepted, except -1 for infinite loopcount\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							break;
						}
					}
				}
				else
				{
					if (*optarg == '-') {
						sprintf(temp_string, "Option '--%s', Negative values are not accepted\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						break;
					}
				}
			}
			else if (((opt == 0) && (*long_options[long_index].flag == 22)) || ((opt == 0) && (*long_options[long_index].flag == 31)))
			{
				if((opt == 0) && (*long_options[long_index].flag == 22))
				{
					if ((optind + 3) >= argc ) {
						sprintf(temp_string, "\nOption '--%s' requires following FIVE arguments [SOCKET] [DIMM_ADDR] [LID] [OFFSET] [REGSPACE]. \n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
					if (*argv[optind] == '-' || *argv[optind + 1] == '-' ||
						*argv[optind + 2] == '-' || *argv[optind + 3] == '-') {
						sprintf(temp_string, "Negative values are not accepted\n");
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				}
				else if((opt == 0) && (*long_options[long_index].flag == 31))
				{
					if ((optind + 4) >= argc ) {
						sprintf(temp_string, "\nOption '--%s' requires following SIX arguments [SOCKET] [DIMM_ADDR] [LID] [OFFSET] [REGSPACE] [REGDATA]. \n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
					if (*argv[optind] == '-' || *argv[optind + 1] == '-' ||
						*argv[optind + 2] == '-' || *argv[optind + 3] == '-' || *argv[optind + 4] == '-') {
						sprintf(temp_string, "Negative values are not accepted\n");
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				}

				if (is_string_number(argv[optind + 3])) {
					sprintf(temp_string, "Option '--%s' requires REGSPACE as valid"
						" numeric value\n\n", long_options[long_index].name);
					append_string(&err_string, temp_string);
					show_usage_on_err = true;
					break;
				}
				if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
				{
					input_data = strtoul(argv[optind], &end, 16);
					if (*end) {
						sprintf(temp_string, "Option '--%s' requires DIMM_ADDR as valid numeric value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				} else {
					if (is_string_number(argv[optind])) {
						sprintf(temp_string, "Option '--%s' requires DIMM_ADDR argument as valid numeric value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
					input_data = atol(argv[optind]);
				}
				if (!strncmp(argv[optind + 1], "0x", 2) || !strncmp(argv[optind + 1], "0X", 2))
				{
					strtoul(argv[optind + 1], &end, 16);
					if (*end) {
						sprintf(temp_string, "Option '--%s' requires LID as valid numeric or Hex value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				} else {
					if (is_string_number(argv[optind + 1])) {
						sprintf(temp_string, "Option '--%s' requires LID argument as valid numeric or Hex value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				}
				if (!strncmp(argv[optind + 2], "0x", 2) || !strncmp(argv[optind + 2], "0X", 2))
				{
					strtoul(argv[optind + 2], &end, 16);
					if (*end) {
						sprintf(temp_string, "Option '--%s' requires OFFSET as valid numeric or Hex value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				} else {
					if (is_string_number(argv[optind + 2])) {
						sprintf(temp_string, "Option '--%s' requires OFFSET argument as valid numeric or Hex value\n\n", long_options[long_index].name);
						append_string(&err_string, temp_string);
						show_usage_on_err = true;
						break;
					}
				}
				if((opt == 0) && (*long_options[long_index].flag == 31))
				{
					if (!strncmp(argv[optind + 3], "0x", 2) || !strncmp(argv[optind + 3], "0X", 2))
					{
						strtoul(argv[optind + 3], &end, 16);
						if (*end) {
							sprintf(temp_string, "Option '--%s' requires REGDATA as valid numeric or Hex value\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
					} else {
						if (is_string_number(argv[optind + 3])) {
							sprintf(temp_string, "Option '--%s' requires REGDATA argument as valid numeric or Hex value\n\n", long_options[long_index].name);
							append_string(&err_string, temp_string);
							show_usage_on_err = true;
							break;
						}
					}
				}
				optind++;//Adding Extra Arguments
				optind++;//Adding Extra Arguments
				optind++;//Adding Extra Arguments
				optind++;//Adding Extra Arguments
				if((opt == 0) && (*long_options[long_index].flag == 31))
				{
					optind++;//Adding Extra Arguments
				}
			}

			if (*long_options[long_index].flag == 14) {
				initial_delay_in_secs = atoi(optarg);
			}
			else if (*long_options[long_index].flag == 15) {
				loop_delay_in_secs = atoi(optarg);
				loop_delay_in_secs_passed = true;
			}
			else if (*long_options[long_index].flag == 16) {
				loop_count = atoi(optarg);
			}
			else if (*long_options[long_index].flag == 17) {
				uint32_t temp = atoi(optarg);
				if((temp != 0) && (print_results == DONT_PRINT_RESULTS))
					print_results = PRINT_RESULTS;
				else if (temp == 0)
					print_results = DONT_PRINT_RESULTS;
			}
			else if (*long_options[long_index].flag == 18) {
				append_string(&log_file_name, optarg);
				log_to_file = LOG_TO_FILE;
				create_log_header = true;
			}
			else if (*long_options[long_index].flag == 19) {
				if(stoploop_file_name) {
					free(stoploop_file_name);
					stoploop_file_name = NULL;
				}
				append_string(&stoploop_file_name, optarg);
			}
			else if (*long_options[long_index].flag == 34) {
				metric_table_delay_in_secs = atoi(optarg);
			}
			else if (*long_options[long_index].flag == 36) {
				if(startmetric_file_name) {
					free(startmetric_file_name);
					startmetric_file_name = NULL;
				}
				append_string(&startmetric_file_name, optarg);
			}
			else if (*long_options[long_index].flag == 37) {
				if(stopmetric_file_name) {
					free(stopmetric_file_name);
					stopmetric_file_name = NULL;
				}
				append_string(&stopmetric_file_name, optarg);
			}

			if ((*long_options[long_index].flag == 14) || (*long_options[long_index].flag == 15) || (*long_options[long_index].flag == 34)) {
				if (optind < argc) {
					double temp_var = 0;
					if (!strncmp(argv[optind], "ms", 2) || !strncmp(argv[optind], "MS", 2) || !strncmp(argv[optind], "Ms", 2) || !strncmp(argv[optind], "mS", 2)) {
						temp_var = .001;
					}
					else if (!strncmp(argv[optind], "s", 1) || !strncmp(argv[optind], "S", 1)) {
						temp_var = 1;
					}
					else if (!strncmp(argv[optind], "m", 1) || !strncmp(argv[optind], "M", 1)) {
						temp_var = 60;
					}
					else if (!strncmp(argv[optind], "h", 1) || !strncmp(argv[optind], "H", 1)) {
						temp_var = 3600;
					}
					else if (!strncmp(argv[optind], "d", 1) || !strncmp(argv[optind], "D", 1)) {
						temp_var = 86400;
					}

					if (0 != temp_var) {
						if((*long_options[long_index].flag == 14) && initial_delay_in_secs) initial_delay_in_secs = initial_delay_in_secs*temp_var;
						if((*long_options[long_index].flag == 15) && loop_delay_in_secs) loop_delay_in_secs = loop_delay_in_secs*temp_var;
						if((*long_options[long_index].flag == 34) && metric_table_delay_in_secs) metric_table_delay_in_secs = metric_table_delay_in_secs*temp_var;
						optind++;
					}
				}
			}
		}

		if (getuid() != 0) {
			switch (opt) {
				// Below options requires sudo permissions to run
				case 0:
					if (!((*long_options[long_index].flag == 6)  || (*long_options[long_index].flag == 8)  ||
					      (*long_options[long_index].flag == 27) || (*long_options[long_index].flag == 28) ||
						  (*long_options[long_index].flag == 29) || (*long_options[long_index].flag == 30) ||
						  (*long_options[long_index].flag == 31) || (*long_options[long_index].flag == 33)
						))
					{
						continue;
					}
				case 'C':
				case 'A':
				case 'a':
				case 'b':
				case 'e':
				case 's':
				case 'y':
				case 'u':
				case 'w':
				case 'l':
				case 'k':
				case 'j':
				case 'X':
				case 'W':
				case 'n':
				case 'E':
				case 'F':
				case 'P':
					args[0] = sudostr;
					args[1] = argv[0];
					for (i = 0; i < argc; i++) {
						args[i + 1] = argv[i];
					}
					args[i + 1] = NULL;
					execvp("sudo", args);
			}
		}
	}

	show_smi_message();
	if(err_string)
	{
		printf(MAG "%s" RESET, err_string);

		if(show_usage_on_err)
			show_usage(argv[0]);

		free(err_string);
		err_string = NULL;

		return ret_on_err;
	}

	if (argc <= 1) {
		ret = show_smi_all_parameters();
		printf(MAG"\nTry `%s --help' for more information." RESET
			"\n\n", argv[0]);
	}

	if(initial_delay_in_secs) {
		if(print_results == PRINT_RESULTS) printf("\n* InitialDelay(in secs):%f, ...\n", initial_delay_in_secs);
		sleep_in_milliseconds(initial_delay_in_secs*1000);
	}

	bool metric_table_requested = false;
	for(int loopcount = 0; loopcount < MAX_SOCKET; loopcount++)
	{
		if(show_metric_table[loopcount])
		{
			if(false == metric_table_requested)
			{
				if(startmetric_file_name/* && loop_count*/) {
					int temp_loop_counter = 0;
					while(1)
					{
						sleep_in_milliseconds(200);

						FILE *temp_file_fp =  NULL;
						temp_file_fp = fopen(startmetric_file_name, "r");
						if(temp_file_fp != NULL) {
							if(print_results == PRINT_RESULTS) printf("StartMetric file \"%s\" is present, ...\n", startmetric_file_name);
							fclose(temp_file_fp);
							temp_file_fp = NULL;
							break;
						}
						else
						{
							if((print_results == PRINT_RESULTS) && (0 == temp_loop_counter))
								printf("StartMetric file \"%s\" is not present, waiting for file \"%s\" to be created to Start Metric Collection, ...\n", startmetric_file_name, startmetric_file_name);
						}
						temp_loop_counter++;
					}
				}
			}
			ret = epyc_show_metric_table(loopcount, metric_table_delay_in_secs*1000, true, false);
			if(ret != ESMI_SUCCESS)
				show_metric_table[loopcount] = false;
			metric_table_requested = true;
		}
	}
	if(metric_table_requested)
	{
		if((print_results == PRINT_RESULTS) && ((0 != metric_table_delay_in_secs) || (startmetric_file_name))/*&& (NULL == log_file_name)*/)
		{
			printf("Started Metric Collection, ");
			if(0 != metric_table_delay_in_secs)
				printf("MetricDelay(in secs):%f, ", (double)metric_table_delay_in_secs);
			printf(" ...\n");
		}
		sleep_in_milliseconds(metric_table_delay_in_secs?metric_table_delay_in_secs*1000:metric_table_default_delay_in_ms);
	}

	int loop_counter = 0;
	char date_buffer[100];
	char time_buffer[100];

	if(log_file_name) {
		// Check if log_file_name already exists
		bool log_file_exists = false;
		FILE *check_fp = fopen(log_file_name, "r");
		if (check_fp != NULL) {
			log_file_exists = true;
			fclose(check_fp);
		}
		if(log_file_exists)
		{
			char date_buffer[100];
			char time_buffer[100];

			time_t current_time = time (0);
			strftime (date_buffer, 100, "_%Y_%m%d_", localtime (&current_time));
			strftime (time_buffer, 100, "%H%M%S", localtime (&current_time));

			char *dot = strrchr(log_file_name, '.');
			if (dot) {
				size_t name_len = dot - log_file_name;
				char *new_name = malloc(name_len + strlen(date_buffer) + strlen(time_buffer) + strlen(dot) + 1);
				if (new_name) {
					strncpy(new_name, log_file_name, name_len);
					new_name[name_len] = '\0';
					strcat(new_name, date_buffer);
					strcat(new_name, time_buffer);
					strcat(new_name, dot);
					free(log_file_name);
					log_file_name = new_name;
				}
			} else {
				append_string(&log_file_name, date_buffer);
				append_string(&log_file_name, time_buffer);
			}
		}
	}

	//Loop Of Commands Starts
	do {
	FILE *log_file_fp =  NULL;
	if(log_file_name) {
		log_file_fp = fopen(log_file_name, "a");
		if(log_file_fp == NULL) {
			printf("Error: Failed to open log file '%s'", log_file_name);
			return ESMI_FILE_ERROR;
		}
	}
	FILE *temp_file_fp =  NULL;
	if(stoploop_file_name && loop_count) {
		temp_file_fp = fopen(stoploop_file_name, "r");
		if(temp_file_fp != NULL) {
			if(print_results == PRINT_RESULTS) printf("stoploop file \"%s\" is present, hence stopped loop\n", stoploop_file_name);
			fclose(temp_file_fp);
			temp_file_fp = NULL;
			break;
		}
	}

	if(loop_count) {
		if(loop_delay_in_secs_passed == false) loop_delay_in_secs = 1;
		if(print_results == PRINT_RESULTS) printf("\n* LoopCount:%d, LoopDelay(in secs):%f, ...\n",loop_counter, loop_delay_in_secs);
		if(loop_delay_in_secs) sleep_in_milliseconds(loop_delay_in_secs*1000);
	}

	struct timeval tp;
	gettimeofday(&tp, NULL);
	long int ms = tp.tv_usec / 1000;

	time_t current_time = time (0);
	strftime (date_buffer, 100, "%Y-%m-%d", localtime (&current_time));
	strftime (time_buffer, 100, "%H:%M:%S", localtime (&current_time));
	char temp_string[100] = {'\0'};
	sprintf(temp_string, ":%lu", ms);
	strcat(time_buffer, temp_string);

	if(loop_count) {
		if(print_results == PRINT_RESULTS) printf ("* CurrentTime:%s,%s\n", date_buffer, time_buffer);
	}

	if(metric_table_requested)
	{
		for(int loopcount = 0; loopcount < MAX_SOCKET; loopcount++)
		{
			if(show_metric_table[loopcount])
			{
				ret = epyc_show_metric_table(loopcount, metric_table_delay_in_secs*1000, false, false);
			}
		}
	}

	optind = 0;
	while ((opt = getopt_long(argc, argv, helperstring,
			long_options, &long_index)) != -1) {

	if (opt == 'N' || opt == 'H' || opt == 'g' || opt == 'T')
	{
		if (!strncmp(argv[optind], "0x", 2) || !strncmp(argv[optind], "0X", 2))
		{
			input_data = strtoul(argv[optind++], &end, 16);//Adding Extra Arguments
		} else {
			input_data = atol(argv[optind++]);//Adding Extra Arguments
		}
	}
	else if((opt == 0) && ((*long_options[long_index].flag == 14) || (*long_options[long_index].flag == 15) || (*long_options[long_index].flag == 34)))
	{
		if (optind < argc) {
			if (!strncmp(argv[optind], "ms", 2) || !strncmp(argv[optind], "MS", 2) || !strncmp(argv[optind], "Ms", 2) || !strncmp(argv[optind], "mS", 2) ||
				!strncmp(argv[optind], "s", 1) || !strncmp(argv[optind], "s", 1) ||
				!strncmp(argv[optind], "m", 1) || !strncmp(argv[optind], "M", 1) ||
				!strncmp(argv[optind], "h", 1) || !strncmp(argv[optind], "H", 1) ||
				!strncmp(argv[optind], "d", 1) || !strncmp(argv[optind], "D", 1)) {
				optind++;//Adding Extra Arguments
			}
		}
	}
	switch (opt) {
		case 0:
			ret = parse_flag_based_options(argv, *long_options[long_index].flag);
			break;
		case 'e' :
			/* Get the energy for a given core index */
			core_id = atoi(optarg);
			ret = epyc_get_coreenergy(core_id);
			break;
		case 's' :
			/* Get the energy metrics for all sockets */
			ret = epyc_get_sockenergy();
			break;
		case 'p' :
			/* Get the Power metrics for all sockets */
			ret = epyc_get_socketpower();
			break;
		case 'd' :
			/* Get DDR bandwidth details */
			ret = epyc_get_ddr_bw();
			break;
		case 'f' :
			/* Get SMU Firmware version */
			ret = epyc_get_smu_fw_version();
			break;
		case 'o' :
			/* Get HSMP driver version */
			ret = epyc_get_hsmp_driver_version();
			break;
		case 'v' :
			/* Get HSMP protocol version */
			ret = epyc_get_hsmp_proto_version();
			break;
		case 'x' :
			/* Get HSMP PROCHOT status */
			ret = epyc_get_prochot_status();
			break;
		case 'y' :
			sock_id = atoi(optarg);
			/* Set auto DF Pstate */
			ret = epyc_apb_enable(sock_id);
			break;
		case 'u' :
			sock_id = atoi(optarg);
			pstate = atoi(argv[optind++]);
			/* Set DF Pstate to user specified value */
			ret = epyc_set_df_pstate(sock_id, pstate);
			break;
		case 'z' :
			/* Get Clock Frequencies */
			ret = epyc_get_clock_freq();
			break;
		case 'L' :
			/* Get the Boostlimit for a given core index */
			core_id = atoi(optarg);
			ret = epyc_get_coreperf(core_id);
			break;
		case 'C':
			/* Set the power limit value for a given socket */
			sock_id = atoi(optarg);
			power = atoi(argv[optind++]);
			ret = epyc_setpowerlimit(sock_id, power);
			break;
		case 'a' :
			/* Set the boostlimit value for a given core */
			core_id = atoi(optarg);
			boostlimit = atoi(argv[optind++]);
			ret = epyc_setcoreperf(core_id, boostlimit);
			break;
		case 'b' :
			/* Set the boostlimit value for a given socket */
			sock_id = atoi(optarg);
			boostlimit = atoi(argv[optind++]);
			ret = epyc_setsocketperf(sock_id, boostlimit);
			break;
		case 'r' :
			/* Get the Power values for a given socket */
			sock_id = atoi(optarg);
			ret = epyc_get_sockc0_residency(sock_id);
			break;
		case 't' :
			/* Get socket Temperature*/
			ret = epyc_get_temperature();
			break;

		case 'w' :
			/* Set xgmi link width */
			sock_id = atoi(optarg);
			min = atoi(argv[optind++]);
			max = atoi(argv[optind++]);
			ret = epyc_set_xgmi_width(sock_id, min, max);
			break;
		case 'l' :
			/* Set lclk dpm level */
			sock_id = atoi(optarg);
			nbio_id = atoi(argv[optind++]);
			min = atoi(argv[optind++]);
			max = atoi(argv[optind++]);
			ret = epyc_set_lclk_dpm_level(sock_id, nbio_id, min, max);
			break;
		case 'g' :
			/* Get DIMM power consumption */
			sock_id = atoi(optarg);
			ret = epyc_get_dimm_power(sock_id, input_data);
			break;
		case 'T' :
			/* Get DIMM temp range and refresh rate */
			sock_id = atoi(optarg);
			ret = epyc_get_dimm_temp_range_refresh_rate(sock_id, input_data);
			break;
		case 'H' :
			/* Get DIMM temperature */
			sock_id = atoi(optarg);
			ret = epyc_get_dimm_thermal(sock_id, input_data);
			break;
		case 'q' :
			/* Get the current clock freq limit for a given core */
			core_id = atoi(optarg);
			ret = epyc_get_curr_freq_limit_core(core_id);
			break;
		case 'm' :
			/* Get svi based power telemetry of all rails */
			epyc_get_power_telemetry();
			break;
		case 'i' :
			/* Get xgmi bandiwdth info on specified link */
			sock_id = atoi(optarg);
			link_name = argv[optind++];
			toUpperCaseString(link_name);
			bw_type = argv[optind++];
			toUpperCaseString(bw_type);
			epyc_get_xgmi_bandwidth_info(sock_id, link_name, bw_type);
			break;
		case 'B' :
			/* Get io bandiwdth info on specified link */
			sock_id = atoi(optarg);
			link_name = argv[optind++];
			toUpperCaseString(link_name);
			ret = epyc_get_io_bandwidth_info(sock_id, link_name);
			break;
		case 'n' :
			/* Set gmi3 link width */
			sock_id = atoi(optarg);
			min = atoi(argv[optind++]);
			max = atoi(argv[optind++]);
			ret = epyc_set_gmi3_link_width(sock_id, min, max);
			break;
		case 'j' :
			/* Set rate control on pci3 gen5 capable devices */
			sock_id = atoi(optarg);
			ctrl = atoi(argv[optind++]);
			ret = epyc_set_pciegen5_rate_ctl(sock_id, ctrl);
			break;
		case 'k' :
			/* Set power efficiency  profile policy */
			sock_id = atoi(optarg);
			mode = atoi(argv[optind++]);
			if(((0x1A == sys_info.family) && ((sys_info.model >= 0x50) && (sys_info.model <= 0x5F))) && ((mode == POWER_EFFICIENCY_MODE_4)||(mode == POWER_EFFICIENCY_MODE_5)))
			{
				pwreffmode_util = atoi(argv[optind++]);
				pwreffmode_pptlimit = atoi(argv[optind++]);
			}
			ret = epyc_set_power_efficiency_mode(sock_id, mode, &pwreffmode_util, &pwreffmode_pptlimit);
			break;
		case 'X' :
			/* Set df pstate range */
			sock_id = atoi(optarg);
			min = atoi(argv[optind++]);
			max = atoi(argv[optind++]);
			ret = epyc_set_df_pstate_range(sock_id, min, max);
			break;
		case 'Y' :
			/* Get lclk DPM level for a given nbio and a socket */
			sock_id = atoi(optarg);
			nbio_id = atoi(argv[optind++]);
			epyc_get_lclk_dpm_level(sock_id, nbio_id);
			break;
		case 'Q' :
			/* Get the current clock freq limit for a given socket */
			sock_id = atoi(optarg);
			ret = epyc_get_curr_freq_limit_socket(sock_id);
			break;
		case 'D' :
			/* Get Metrics Table version */
			ret = epyc_get_metrics_table_version();
			break;
		case 'J' :
			/* Get Metrics Table */
			sock_id = atoi(optarg);
			if(true == show_metric_table[sock_id])
				ret = epyc_show_metric_table(sock_id, metric_table_delay_in_secs*1000, false, true);
			break;
		case 'N' :
			/* Test HSMP mailbox i/f */
			sock_id = atoi(optarg);
			if (input_data > UINT_MAX) {
				printf(RED "%s: option '-%c' requires input argument"
					" to be less than 0x%x"
					RESET "\n\n", argv[0], opt, UINT_MAX);
				break;
			}
			ret = test_hsmp_mailbox(sock_id, input_data);
			break;
		case 'O' :
			sock_id = atoi(optarg);
			ret = epyc_get_pwr_efficiency_mode(sock_id);
			break;
		case 'E' :
			sock_id = atoi(optarg);
			min = atoi(argv[optind++]);
			max = atoi(argv[optind++]);
			ret = epyc_set_xgmi_pstate_range(sock_id, min, max);
			break;
		case 'F' :
			sock_id = atoi(optarg);
			value = atoi(argv[optind++]);
			ret = epyc_set_cpu_iso_freq_policy(sock_id, value);
			break;
		case 'P' :
			sock_id = atoi(optarg);
			value = atoi(argv[optind++]);
			ret = epyc_set_dfc_ctrl(sock_id, value);
			break;
		case 'M' :
			sock_id = atoi(optarg);
			ret = epyc_get_dfc_ctrl(sock_id);
			break;
		case 'K' :
			sock_id = atoi(optarg);
			ret = epyc_get_cpu_iso_freq_policy(sock_id);
			break;
		case 'A' :
			ret = show_smi_all_parameters();
			ret = ESMI_SUCCESS;
			break;
		case 'h' :
			show_usage(argv[0]);
			ret = ESMI_SUCCESS;
			break;
		case 'V' :
			print_esmi_version();
			ret = ESMI_SUCCESS;
			break;
		case 'W' :
			write_msr_allowlist_file();
			ret = ESMI_SUCCESS;
			break;
		case ':' :
			/* missing option argument */
			printf(RED "%s: option '-%c' requires an argument."
				RESET "\n\n", argv[0], opt);
			break;
		case '?':
			if (isprint(opt)) {
				printf(MAG "Try `%s --help' for more"
				" information." RESET "\n", argv[0]);
			} else {
				printf("Unknown option character"
				" `\\x%x'.\n", opt);
			}
			break;
		default:
			printf(MAG "Try `%s --help' for more information."
						RESET "\n\n", argv[0]);
			break;
		} // end of Switch
	}
	if (optind < argc) {
		printf(RED"\nExtra Non-option argument<s> passed : %s"
			RESET "\n", argv[optind]);
		printf(MAG "Try `%s --help' for more information."
					RESET "\n\n", argv[0]);
	}
	if(log_file_fp)
	{
		if(create_log_header) {
			fprintf(log_file_fp, "Date,Timestamp,%s\n", log_file_header);
			create_log_header = false;
		}
		fprintf(log_file_fp, "%s,%s,%s\n", date_buffer, time_buffer, log_file_data);
		fclose(log_file_fp);
		if(log_file_data) {
			free(log_file_data);
			log_file_data = NULL;
		}
	}
	loop_counter++;
	} while((loop_count == -1) || (loop_counter < loop_count));

	return ret;
}

int main(int argc, char **argv)
{
	esmi_status_t ret;
	/* Parse command arguments */
	ret = parsesmi_args(argc, argv);

	show_smi_end_message();
	/* Program termination */
	esmi_exit();

	if(log_file_header) {
		free(log_file_header);
		log_file_header = NULL;
	}
	if(log_file_name) {
		free(log_file_name);
		log_file_name = NULL;
	}
	if(stoploop_file_name) {
		free(stoploop_file_name);
		stoploop_file_name = NULL;
	}
	if(startmetric_file_name) {
		free(startmetric_file_name);
		startmetric_file_name = NULL;
	}
	if(stopmetric_file_name) {
		free(stopmetric_file_name);
		stopmetric_file_name = NULL;
	}

	if (features)
		free(features);
	return ret;
}
