18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <unistd.h>
38c2ecf20Sopenharmony_ci#include <stdio.h>
48c2ecf20Sopenharmony_ci#include <errno.h>
58c2ecf20Sopenharmony_ci#include <stdlib.h>
68c2ecf20Sopenharmony_ci#include <limits.h>
78c2ecf20Sopenharmony_ci#include <string.h>
88c2ecf20Sopenharmony_ci#include <ctype.h>
98c2ecf20Sopenharmony_ci#include <getopt.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <cpufreq.h>
128c2ecf20Sopenharmony_ci#include <cpuidle.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "helpers/helpers.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic struct option info_opts[] = {
178c2ecf20Sopenharmony_ci     {"disable",	required_argument,		NULL, 'd'},
188c2ecf20Sopenharmony_ci     {"enable",		required_argument,		NULL, 'e'},
198c2ecf20Sopenharmony_ci     {"disable-by-latency", required_argument,		NULL, 'D'},
208c2ecf20Sopenharmony_ci     {"enable-all",	no_argument,			NULL, 'E'},
218c2ecf20Sopenharmony_ci     { },
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciint cmd_idle_set(int argc, char **argv)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	extern char *optarg;
288c2ecf20Sopenharmony_ci	extern int optind, opterr, optopt;
298c2ecf20Sopenharmony_ci	int ret = 0, cont = 1, param = 0, disabled;
308c2ecf20Sopenharmony_ci	unsigned long long latency = 0, state_latency;
318c2ecf20Sopenharmony_ci	unsigned int cpu = 0, idlestate = 0, idlestates = 0;
328c2ecf20Sopenharmony_ci	char *endptr;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	do {
358c2ecf20Sopenharmony_ci		ret = getopt_long(argc, argv, "d:e:ED:", info_opts, NULL);
368c2ecf20Sopenharmony_ci		if (ret == -1)
378c2ecf20Sopenharmony_ci			break;
388c2ecf20Sopenharmony_ci		switch (ret) {
398c2ecf20Sopenharmony_ci		case '?':
408c2ecf20Sopenharmony_ci			param = '?';
418c2ecf20Sopenharmony_ci			cont = 0;
428c2ecf20Sopenharmony_ci			break;
438c2ecf20Sopenharmony_ci		case 'd':
448c2ecf20Sopenharmony_ci			if (param) {
458c2ecf20Sopenharmony_ci				param = -1;
468c2ecf20Sopenharmony_ci				cont = 0;
478c2ecf20Sopenharmony_ci				break;
488c2ecf20Sopenharmony_ci			}
498c2ecf20Sopenharmony_ci			param = ret;
508c2ecf20Sopenharmony_ci			idlestate = atoi(optarg);
518c2ecf20Sopenharmony_ci			break;
528c2ecf20Sopenharmony_ci		case 'e':
538c2ecf20Sopenharmony_ci			if (param) {
548c2ecf20Sopenharmony_ci				param = -1;
558c2ecf20Sopenharmony_ci				cont = 0;
568c2ecf20Sopenharmony_ci				break;
578c2ecf20Sopenharmony_ci			}
588c2ecf20Sopenharmony_ci			param = ret;
598c2ecf20Sopenharmony_ci			idlestate = atoi(optarg);
608c2ecf20Sopenharmony_ci			break;
618c2ecf20Sopenharmony_ci		case 'D':
628c2ecf20Sopenharmony_ci			if (param) {
638c2ecf20Sopenharmony_ci				param = -1;
648c2ecf20Sopenharmony_ci				cont = 0;
658c2ecf20Sopenharmony_ci				break;
668c2ecf20Sopenharmony_ci			}
678c2ecf20Sopenharmony_ci			param = ret;
688c2ecf20Sopenharmony_ci			latency = strtoull(optarg, &endptr, 10);
698c2ecf20Sopenharmony_ci			if (*endptr != '\0') {
708c2ecf20Sopenharmony_ci				printf(_("Bad latency value: %s\n"), optarg);
718c2ecf20Sopenharmony_ci				exit(EXIT_FAILURE);
728c2ecf20Sopenharmony_ci			}
738c2ecf20Sopenharmony_ci			break;
748c2ecf20Sopenharmony_ci		case 'E':
758c2ecf20Sopenharmony_ci			if (param) {
768c2ecf20Sopenharmony_ci				param = -1;
778c2ecf20Sopenharmony_ci				cont = 0;
788c2ecf20Sopenharmony_ci				break;
798c2ecf20Sopenharmony_ci			}
808c2ecf20Sopenharmony_ci			param = ret;
818c2ecf20Sopenharmony_ci			break;
828c2ecf20Sopenharmony_ci		case -1:
838c2ecf20Sopenharmony_ci			cont = 0;
848c2ecf20Sopenharmony_ci			break;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci	} while (cont);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	switch (param) {
898c2ecf20Sopenharmony_ci	case -1:
908c2ecf20Sopenharmony_ci		printf(_("You can't specify more than one "
918c2ecf20Sopenharmony_ci			 "output-specific argument\n"));
928c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
938c2ecf20Sopenharmony_ci	case '?':
948c2ecf20Sopenharmony_ci		printf(_("invalid or unknown argument\n"));
958c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Default is: set all CPUs */
998c2ecf20Sopenharmony_ci	if (bitmask_isallclear(cpus_chosen))
1008c2ecf20Sopenharmony_ci		bitmask_setall(cpus_chosen);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (cpu = bitmask_first(cpus_chosen);
1038c2ecf20Sopenharmony_ci	     cpu <= bitmask_last(cpus_chosen); cpu++) {
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		if (!bitmask_isbitset(cpus_chosen, cpu))
1068c2ecf20Sopenharmony_ci			continue;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if (cpupower_is_cpu_online(cpu) != 1)
1098c2ecf20Sopenharmony_ci			continue;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		idlestates = cpuidle_state_count(cpu);
1128c2ecf20Sopenharmony_ci		if (idlestates <= 0)
1138c2ecf20Sopenharmony_ci			continue;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		switch (param) {
1168c2ecf20Sopenharmony_ci		case 'd':
1178c2ecf20Sopenharmony_ci			ret = cpuidle_state_disable(cpu, idlestate, 1);
1188c2ecf20Sopenharmony_ci			if (ret == 0)
1198c2ecf20Sopenharmony_ci		printf(_("Idlestate %u disabled on CPU %u\n"),  idlestate, cpu);
1208c2ecf20Sopenharmony_ci			else if (ret == -1)
1218c2ecf20Sopenharmony_ci		printf(_("Idlestate %u not available on CPU %u\n"),
1228c2ecf20Sopenharmony_ci		       idlestate, cpu);
1238c2ecf20Sopenharmony_ci			else if (ret == -2)
1248c2ecf20Sopenharmony_ci		printf(_("Idlestate disabling not supported by kernel\n"));
1258c2ecf20Sopenharmony_ci			else
1268c2ecf20Sopenharmony_ci		printf(_("Idlestate %u not disabled on CPU %u\n"),
1278c2ecf20Sopenharmony_ci		       idlestate, cpu);
1288c2ecf20Sopenharmony_ci			break;
1298c2ecf20Sopenharmony_ci		case 'e':
1308c2ecf20Sopenharmony_ci			ret = cpuidle_state_disable(cpu, idlestate, 0);
1318c2ecf20Sopenharmony_ci			if (ret == 0)
1328c2ecf20Sopenharmony_ci		printf(_("Idlestate %u enabled on CPU %u\n"),  idlestate, cpu);
1338c2ecf20Sopenharmony_ci			else if (ret == -1)
1348c2ecf20Sopenharmony_ci		printf(_("Idlestate %u not available on CPU %u\n"),
1358c2ecf20Sopenharmony_ci		       idlestate, cpu);
1368c2ecf20Sopenharmony_ci			else if (ret == -2)
1378c2ecf20Sopenharmony_ci		printf(_("Idlestate enabling not supported by kernel\n"));
1388c2ecf20Sopenharmony_ci			else
1398c2ecf20Sopenharmony_ci		printf(_("Idlestate %u not enabled on CPU %u\n"),
1408c2ecf20Sopenharmony_ci		       idlestate, cpu);
1418c2ecf20Sopenharmony_ci			break;
1428c2ecf20Sopenharmony_ci		case 'D':
1438c2ecf20Sopenharmony_ci			for (idlestate = 0; idlestate < idlestates; idlestate++) {
1448c2ecf20Sopenharmony_ci				disabled = cpuidle_is_state_disabled
1458c2ecf20Sopenharmony_ci					(cpu, idlestate);
1468c2ecf20Sopenharmony_ci				state_latency = cpuidle_state_latency
1478c2ecf20Sopenharmony_ci					(cpu, idlestate);
1488c2ecf20Sopenharmony_ci				if (disabled == 1) {
1498c2ecf20Sopenharmony_ci					if (latency > state_latency){
1508c2ecf20Sopenharmony_ci						ret = cpuidle_state_disable
1518c2ecf20Sopenharmony_ci							(cpu, idlestate, 0);
1528c2ecf20Sopenharmony_ci						if (ret == 0)
1538c2ecf20Sopenharmony_ci		printf(_("Idlestate %u enabled on CPU %u\n"),  idlestate, cpu);
1548c2ecf20Sopenharmony_ci					}
1558c2ecf20Sopenharmony_ci					continue;
1568c2ecf20Sopenharmony_ci				}
1578c2ecf20Sopenharmony_ci				if (latency <= state_latency){
1588c2ecf20Sopenharmony_ci					ret = cpuidle_state_disable
1598c2ecf20Sopenharmony_ci						(cpu, idlestate, 1);
1608c2ecf20Sopenharmony_ci					if (ret == 0)
1618c2ecf20Sopenharmony_ci		printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu);
1628c2ecf20Sopenharmony_ci				}
1638c2ecf20Sopenharmony_ci			}
1648c2ecf20Sopenharmony_ci			break;
1658c2ecf20Sopenharmony_ci		case 'E':
1668c2ecf20Sopenharmony_ci			for (idlestate = 0; idlestate < idlestates; idlestate++) {
1678c2ecf20Sopenharmony_ci				disabled = cpuidle_is_state_disabled
1688c2ecf20Sopenharmony_ci					(cpu, idlestate);
1698c2ecf20Sopenharmony_ci				if (disabled == 1) {
1708c2ecf20Sopenharmony_ci					ret = cpuidle_state_disable
1718c2ecf20Sopenharmony_ci						(cpu, idlestate, 0);
1728c2ecf20Sopenharmony_ci					if (ret == 0)
1738c2ecf20Sopenharmony_ci		printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu);
1748c2ecf20Sopenharmony_ci				}
1758c2ecf20Sopenharmony_ci			}
1768c2ecf20Sopenharmony_ci			break;
1778c2ecf20Sopenharmony_ci		default:
1788c2ecf20Sopenharmony_ci			/* Not reachable with proper args checking */
1798c2ecf20Sopenharmony_ci			printf(_("Invalid or unknown argument\n"));
1808c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
1818c2ecf20Sopenharmony_ci			break;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	return EXIT_SUCCESS;
1858c2ecf20Sopenharmony_ci}
186