18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
48c2ecf20Sopenharmony_ci *  (C) 2010       Thomas Renninger <trenn@suse.de>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <unistd.h>
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include <errno.h>
118c2ecf20Sopenharmony_ci#include <stdlib.h>
128c2ecf20Sopenharmony_ci#include <string.h>
138c2ecf20Sopenharmony_ci#include <getopt.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <cpuidle.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "helpers/sysfs.h"
188c2ecf20Sopenharmony_ci#include "helpers/helpers.h"
198c2ecf20Sopenharmony_ci#include "helpers/bitmask.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define LINE_LEN 10
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic void cpuidle_cpu_output(unsigned int cpu, int verbose)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	unsigned int idlestates, idlestate;
268c2ecf20Sopenharmony_ci	char *tmp;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	idlestates = cpuidle_state_count(cpu);
298c2ecf20Sopenharmony_ci	if (idlestates == 0) {
308c2ecf20Sopenharmony_ci		printf(_("CPU %u: No idle states\n"), cpu);
318c2ecf20Sopenharmony_ci		return;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	printf(_("Number of idle states: %d\n"), idlestates);
358c2ecf20Sopenharmony_ci	printf(_("Available idle states:"));
368c2ecf20Sopenharmony_ci	for (idlestate = 0; idlestate < idlestates; idlestate++) {
378c2ecf20Sopenharmony_ci		tmp = cpuidle_state_name(cpu, idlestate);
388c2ecf20Sopenharmony_ci		if (!tmp)
398c2ecf20Sopenharmony_ci			continue;
408c2ecf20Sopenharmony_ci		printf(" %s", tmp);
418c2ecf20Sopenharmony_ci		free(tmp);
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	printf("\n");
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (!verbose)
468c2ecf20Sopenharmony_ci		return;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	for (idlestate = 0; idlestate < idlestates; idlestate++) {
498c2ecf20Sopenharmony_ci		int disabled = cpuidle_is_state_disabled(cpu, idlestate);
508c2ecf20Sopenharmony_ci		/* Disabled interface not supported on older kernels */
518c2ecf20Sopenharmony_ci		if (disabled < 0)
528c2ecf20Sopenharmony_ci			disabled = 0;
538c2ecf20Sopenharmony_ci		tmp = cpuidle_state_name(cpu, idlestate);
548c2ecf20Sopenharmony_ci		if (!tmp)
558c2ecf20Sopenharmony_ci			continue;
568c2ecf20Sopenharmony_ci		printf("%s%s:\n", tmp, (disabled) ? " (DISABLED) " : "");
578c2ecf20Sopenharmony_ci		free(tmp);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		tmp = cpuidle_state_desc(cpu, idlestate);
608c2ecf20Sopenharmony_ci		if (!tmp)
618c2ecf20Sopenharmony_ci			continue;
628c2ecf20Sopenharmony_ci		printf(_("Flags/Description: %s\n"), tmp);
638c2ecf20Sopenharmony_ci		free(tmp);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		printf(_("Latency: %lu\n"),
668c2ecf20Sopenharmony_ci		       cpuidle_state_latency(cpu, idlestate));
678c2ecf20Sopenharmony_ci		printf(_("Usage: %lu\n"),
688c2ecf20Sopenharmony_ci		       cpuidle_state_usage(cpu, idlestate));
698c2ecf20Sopenharmony_ci		printf(_("Duration: %llu\n"),
708c2ecf20Sopenharmony_ci		       cpuidle_state_time(cpu, idlestate));
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void cpuidle_general_output(void)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	char *tmp;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	tmp = cpuidle_get_driver();
798c2ecf20Sopenharmony_ci	if (!tmp) {
808c2ecf20Sopenharmony_ci		printf(_("Could not determine cpuidle driver\n"));
818c2ecf20Sopenharmony_ci		return;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	printf(_("CPUidle driver: %s\n"), tmp);
858c2ecf20Sopenharmony_ci	free(tmp);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	tmp = cpuidle_get_governor();
888c2ecf20Sopenharmony_ci	if (!tmp) {
898c2ecf20Sopenharmony_ci		printf(_("Could not determine cpuidle governor\n"));
908c2ecf20Sopenharmony_ci		return;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	printf(_("CPUidle governor: %s\n"), tmp);
948c2ecf20Sopenharmony_ci	free(tmp);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void proc_cpuidle_cpu_output(unsigned int cpu)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	long max_allowed_cstate = 2000000000;
1008c2ecf20Sopenharmony_ci	unsigned int cstate, cstates;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	cstates = cpuidle_state_count(cpu);
1038c2ecf20Sopenharmony_ci	if (cstates == 0) {
1048c2ecf20Sopenharmony_ci		printf(_("CPU %u: No C-states info\n"), cpu);
1058c2ecf20Sopenharmony_ci		return;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	printf(_("active state:            C0\n"));
1098c2ecf20Sopenharmony_ci	printf(_("max_cstate:              C%u\n"), cstates-1);
1108c2ecf20Sopenharmony_ci	printf(_("maximum allowed latency: %lu usec\n"), max_allowed_cstate);
1118c2ecf20Sopenharmony_ci	printf(_("states:\t\n"));
1128c2ecf20Sopenharmony_ci	for (cstate = 1; cstate < cstates; cstate++) {
1138c2ecf20Sopenharmony_ci		printf(_("    C%d:                  "
1148c2ecf20Sopenharmony_ci			 "type[C%d] "), cstate, cstate);
1158c2ecf20Sopenharmony_ci		printf(_("promotion[--] demotion[--] "));
1168c2ecf20Sopenharmony_ci		printf(_("latency[%03lu] "),
1178c2ecf20Sopenharmony_ci		       cpuidle_state_latency(cpu, cstate));
1188c2ecf20Sopenharmony_ci		printf(_("usage[%08lu] "),
1198c2ecf20Sopenharmony_ci		       cpuidle_state_usage(cpu, cstate));
1208c2ecf20Sopenharmony_ci		printf(_("duration[%020Lu] \n"),
1218c2ecf20Sopenharmony_ci		       cpuidle_state_time(cpu, cstate));
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic struct option info_opts[] = {
1268c2ecf20Sopenharmony_ci	{"silent", no_argument, NULL, 's'},
1278c2ecf20Sopenharmony_ci	{"proc", no_argument, NULL, 'o'},
1288c2ecf20Sopenharmony_ci	{ },
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline void cpuidle_exit(int fail)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	exit(EXIT_FAILURE);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciint cmd_idle_info(int argc, char **argv)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	extern char *optarg;
1398c2ecf20Sopenharmony_ci	extern int optind, opterr, optopt;
1408c2ecf20Sopenharmony_ci	int ret = 0, cont = 1, output_param = 0, verbose = 1;
1418c2ecf20Sopenharmony_ci	unsigned int cpu = 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	do {
1448c2ecf20Sopenharmony_ci		ret = getopt_long(argc, argv, "os", info_opts, NULL);
1458c2ecf20Sopenharmony_ci		if (ret == -1)
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci		switch (ret) {
1488c2ecf20Sopenharmony_ci		case '?':
1498c2ecf20Sopenharmony_ci			output_param = '?';
1508c2ecf20Sopenharmony_ci			cont = 0;
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci		case 's':
1538c2ecf20Sopenharmony_ci			verbose = 0;
1548c2ecf20Sopenharmony_ci			break;
1558c2ecf20Sopenharmony_ci		case -1:
1568c2ecf20Sopenharmony_ci			cont = 0;
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		case 'o':
1598c2ecf20Sopenharmony_ci			if (output_param) {
1608c2ecf20Sopenharmony_ci				output_param = -1;
1618c2ecf20Sopenharmony_ci				cont = 0;
1628c2ecf20Sopenharmony_ci				break;
1638c2ecf20Sopenharmony_ci			}
1648c2ecf20Sopenharmony_ci			output_param = ret;
1658c2ecf20Sopenharmony_ci			break;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci	} while (cont);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	switch (output_param) {
1708c2ecf20Sopenharmony_ci	case -1:
1718c2ecf20Sopenharmony_ci		printf(_("You can't specify more than one "
1728c2ecf20Sopenharmony_ci			 "output-specific argument\n"));
1738c2ecf20Sopenharmony_ci		cpuidle_exit(EXIT_FAILURE);
1748c2ecf20Sopenharmony_ci	case '?':
1758c2ecf20Sopenharmony_ci		printf(_("invalid or unknown argument\n"));
1768c2ecf20Sopenharmony_ci		cpuidle_exit(EXIT_FAILURE);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Default is: show output of CPU 0 only */
1808c2ecf20Sopenharmony_ci	if (bitmask_isallclear(cpus_chosen))
1818c2ecf20Sopenharmony_ci		bitmask_setbit(cpus_chosen, 0);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (output_param == 0)
1848c2ecf20Sopenharmony_ci		cpuidle_general_output();
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	for (cpu = bitmask_first(cpus_chosen);
1878c2ecf20Sopenharmony_ci	     cpu <= bitmask_last(cpus_chosen); cpu++) {
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (!bitmask_isbitset(cpus_chosen, cpu))
1908c2ecf20Sopenharmony_ci			continue;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		printf(_("analyzing CPU %d:\n"), cpu);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		if (sysfs_is_cpu_online(cpu) != 1) {
1958c2ecf20Sopenharmony_ci			printf(_(" *is offline\n"));
1968c2ecf20Sopenharmony_ci			printf("\n");
1978c2ecf20Sopenharmony_ci			continue;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		switch (output_param) {
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		case 'o':
2038c2ecf20Sopenharmony_ci			proc_cpuidle_cpu_output(cpu);
2048c2ecf20Sopenharmony_ci			break;
2058c2ecf20Sopenharmony_ci		case 0:
2068c2ecf20Sopenharmony_ci			printf("\n");
2078c2ecf20Sopenharmony_ci			cpuidle_cpu_output(cpu, verbose);
2088c2ecf20Sopenharmony_ci			break;
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci		printf("\n");
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	return EXIT_SUCCESS;
2138c2ecf20Sopenharmony_ci}
214