diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:05 +0200 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:18 +0200 | 
| commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
| tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /tools/power/cpupower/utils/cpufreq-set.c | |
| parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
| parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) | |
| download | olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.tar.xz olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.zip  | |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'tools/power/cpupower/utils/cpufreq-set.c')
| -rw-r--r-- | tools/power/cpupower/utils/cpufreq-set.c | 331 | 
1 files changed, 331 insertions, 0 deletions
diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c new file mode 100644 index 00000000000..dd1539eb8c6 --- /dev/null +++ b/tools/power/cpupower/utils/cpufreq-set.c @@ -0,0 +1,331 @@ +/* + *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de> + * + *  Licensed under the terms of the GNU GPL License version 2. + */ + + +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <ctype.h> + +#include <getopt.h> + +#include "cpufreq.h" +#include "helpers/helpers.h" + +#define NORM_FREQ_LEN 32 + +static struct option set_opts[] = { +	{ .name = "min",	.has_arg = required_argument,	.flag = NULL,	.val = 'd'}, +	{ .name = "max",	.has_arg = required_argument,	.flag = NULL,	.val = 'u'}, +	{ .name = "governor",	.has_arg = required_argument,	.flag = NULL,	.val = 'g'}, +	{ .name = "freq",	.has_arg = required_argument,	.flag = NULL,	.val = 'f'}, +	{ .name = "related",	.has_arg = no_argument,		.flag = NULL,	.val='r'}, +	{ }, +}; + +static void print_error(void) +{ +	printf(_("Error setting new values. Common errors:\n" +			"- Do you have proper administration rights? (super-user?)\n" +			"- Is the governor you requested available and modprobed?\n" +			"- Trying to set an invalid policy?\n" +			"- Trying to set a specific frequency, but userspace governor is not available,\n" +			"   for example because of hardware which cannot be set to a specific frequency\n" +			"   or because the userspace governor isn't loaded?\n")); +}; + +struct freq_units { +	char		*str_unit; +	int		power_of_ten; +}; + +const struct freq_units def_units[] = { +	{"hz", -3}, +	{"khz", 0}, /* default */ +	{"mhz", 3}, +	{"ghz", 6}, +	{"thz", 9}, +	{NULL, 0} +}; + +static void print_unknown_arg(void) +{ +	printf(_("invalid or unknown argument\n")); +} + +static unsigned long string_to_frequency(const char *str) +{ +	char normalized[NORM_FREQ_LEN]; +	const struct freq_units *unit; +	const char *scan; +	char *end; +	unsigned long freq; +	int power = 0, match_count = 0, i, cp, pad; + +	while (*str == '0') +		str++; + +	for (scan = str; isdigit(*scan) || *scan == '.'; scan++) { +		if (*scan == '.' && match_count == 0) +			match_count = 1; +		else if (*scan == '.' && match_count == 1) +			return 0; +	} + +	if (*scan) { +		match_count = 0; +		for (unit = def_units; unit->str_unit; unit++) { +			for (i = 0; +			     scan[i] && tolower(scan[i]) == unit->str_unit[i]; +			     ++i) +				continue; +			if (scan[i]) +				continue; +			match_count++; +			power = unit->power_of_ten; +		} +		if (match_count != 1) +			return 0; +	} + +	/* count the number of digits to be copied */ +	for (cp = 0; isdigit(str[cp]); cp++) +		continue; + +	if (str[cp] == '.') { +		while (power > -1 && isdigit(str[cp+1])) +			cp++, power--; +	} +	if (power >= -1)	/* not enough => pad */ +		pad = power + 1; +	else			/* to much => strip */ +		pad = 0, cp += power + 1; +	/* check bounds */ +	if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) +		return 0; + +	/* copy digits */ +	for (i = 0; i < cp; i++, str++) { +		if (*str == '.') +			str++; +		normalized[i] = *str; +	} +	/* and pad */ +	for (; i < cp + pad; i++) +		normalized[i] = '0'; + +	/* round up, down ? */ +	match_count = (normalized[i-1] >= '5'); +	/* and drop the decimal part */ +	normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ + +	/* final conversion (and applying rounding) */ +	errno = 0; +	freq = strtoul(normalized, &end, 10); +	if (errno) +		return 0; +	else { +		if (match_count && freq != ULONG_MAX) +			freq++; +		return freq; +	} +} + +static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol) +{ +	struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu); +	int ret; + +	if (!cur_pol) { +		printf(_("wrong, unknown or unhandled CPU?\n")); +		return -EINVAL; +	} + +	if (!new_pol->min) +		new_pol->min = cur_pol->min; + +	if (!new_pol->max) +		new_pol->max = cur_pol->max; + +	if (!new_pol->governor) +		new_pol->governor = cur_pol->governor; + +	ret = cpufreq_set_policy(cpu, new_pol); + +	cpufreq_put_policy(cur_pol); + +	return ret; +} + + +static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol, +		unsigned long freq, unsigned int pc) +{ +	switch (pc) { +	case 0: +		return cpufreq_set_frequency(cpu, freq); + +	case 1: +		/* if only one value of a policy is to be changed, we can +		 * use a "fast path". +		 */ +		if (new_pol->min) +			return cpufreq_modify_policy_min(cpu, new_pol->min); +		else if (new_pol->max) +			return cpufreq_modify_policy_max(cpu, new_pol->max); +		else if (new_pol->governor) +			return cpufreq_modify_policy_governor(cpu, +							new_pol->governor); + +	default: +		/* slow path */ +		return do_new_policy(cpu, new_pol); +	} +} + +int cmd_freq_set(int argc, char **argv) +{ +	extern char *optarg; +	extern int optind, opterr, optopt; +	int ret = 0, cont = 1; +	int double_parm = 0, related = 0, policychange = 0; +	unsigned long freq = 0; +	char gov[20]; +	unsigned int cpu; + +	struct cpufreq_policy new_pol = { +		.min = 0, +		.max = 0, +		.governor = NULL, +	}; + +	/* parameter parsing */ +	do { +		ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL); +		switch (ret) { +		case '?': +			print_unknown_arg(); +			return -EINVAL; +		case -1: +			cont = 0; +			break; +		case 'r': +			if (related) +				double_parm++; +			related++; +			break; +		case 'd': +			if (new_pol.min) +				double_parm++; +			policychange++; +			new_pol.min = string_to_frequency(optarg); +			if (new_pol.min == 0) { +				print_unknown_arg(); +				return -EINVAL; +			} +			break; +		case 'u': +			if (new_pol.max) +				double_parm++; +			policychange++; +			new_pol.max = string_to_frequency(optarg); +			if (new_pol.max == 0) { +				print_unknown_arg(); +				return -EINVAL; +			} +			break; +		case 'f': +			if (freq) +				double_parm++; +			freq = string_to_frequency(optarg); +			if (freq == 0) { +				print_unknown_arg(); +				return -EINVAL; +			} +			break; +		case 'g': +			if (new_pol.governor) +				double_parm++; +			policychange++; +			if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) { +				print_unknown_arg(); +				return -EINVAL; +			} +			if ((sscanf(optarg, "%s", gov)) != 1) { +				print_unknown_arg(); +				return -EINVAL; +			} +			new_pol.governor = gov; +			break; +		} +	} while (cont); + +	/* parameter checking */ +	if (double_parm) { +		printf("the same parameter was passed more than once\n"); +		return -EINVAL; +	} + +	if (freq && policychange) { +		printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n" +				"-g/--governor parameters\n")); +		return -EINVAL; +	} + +	if (!freq && !policychange) { +		printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n" +				"-g/--governor must be passed\n")); +		return -EINVAL; +	} + +	/* Default is: set all CPUs */ +	if (bitmask_isallclear(cpus_chosen)) +		bitmask_setall(cpus_chosen); + +	/* Also set frequency settings for related CPUs if -r is passed */ +	if (related) { +		for (cpu = bitmask_first(cpus_chosen); +		     cpu <= bitmask_last(cpus_chosen); cpu++) { +			struct cpufreq_affected_cpus *cpus; + +			if (!bitmask_isbitset(cpus_chosen, cpu) || +			    cpufreq_cpu_exists(cpu)) +				continue; + +			cpus = cpufreq_get_related_cpus(cpu); +			if (!cpus) +				break; +			while (cpus->next) { +				bitmask_setbit(cpus_chosen, cpus->cpu); +				cpus = cpus->next; +			} +			cpufreq_put_related_cpus(cpus); +		} +	} + + +	/* loop over CPUs */ +	for (cpu = bitmask_first(cpus_chosen); +	     cpu <= bitmask_last(cpus_chosen); cpu++) { + +		if (!bitmask_isbitset(cpus_chosen, cpu) || +		    cpufreq_cpu_exists(cpu)) +			continue; + +		printf(_("Setting cpu: %d\n"), cpu); +		ret = do_one_cpu(cpu, &new_pol, freq, policychange); +		if (ret) +			break; +	} + +	if (ret) +		print_error(); + +	return ret; +}  |