13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci 33d0407baSopenharmony_ci/* 43d0407baSopenharmony_ci * linux/drivers/cpufreq/cpufreq_userspace.c 53d0407baSopenharmony_ci * 63d0407baSopenharmony_ci * Copyright (C) 2001 Russell King 73d0407baSopenharmony_ci * (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> 83d0407baSopenharmony_ci */ 93d0407baSopenharmony_ci 103d0407baSopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 113d0407baSopenharmony_ci 123d0407baSopenharmony_ci#include <linux/cpufreq.h> 133d0407baSopenharmony_ci#include <linux/init.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/mutex.h> 163d0407baSopenharmony_ci#include <linux/slab.h> 173d0407baSopenharmony_ci 183d0407baSopenharmony_cistatic DEFINE_PER_CPU(unsigned int, cpu_is_managed); 193d0407baSopenharmony_cistatic DEFINE_MUTEX(userspace_mutex); 203d0407baSopenharmony_ci 213d0407baSopenharmony_ci/** 223d0407baSopenharmony_ci * cpufreq_set - set the CPU frequency 233d0407baSopenharmony_ci * @policy: pointer to policy struct where freq is being set 243d0407baSopenharmony_ci * @freq: target frequency in kHz 253d0407baSopenharmony_ci * 263d0407baSopenharmony_ci * Sets the CPU frequency to freq. 273d0407baSopenharmony_ci */ 283d0407baSopenharmony_cistatic int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) 293d0407baSopenharmony_ci{ 303d0407baSopenharmony_ci int ret = -EINVAL; 313d0407baSopenharmony_ci unsigned int *setspeed = policy->governor_data; 323d0407baSopenharmony_ci 333d0407baSopenharmony_ci pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); 343d0407baSopenharmony_ci 353d0407baSopenharmony_ci mutex_lock(&userspace_mutex); 363d0407baSopenharmony_ci if (!per_cpu(cpu_is_managed, policy->cpu)) { 373d0407baSopenharmony_ci goto err; 383d0407baSopenharmony_ci } 393d0407baSopenharmony_ci 403d0407baSopenharmony_ci *setspeed = freq; 413d0407baSopenharmony_ci 423d0407baSopenharmony_ci ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); 433d0407baSopenharmony_cierr: 443d0407baSopenharmony_ci mutex_unlock(&userspace_mutex); 453d0407baSopenharmony_ci return ret; 463d0407baSopenharmony_ci} 473d0407baSopenharmony_ci 483d0407baSopenharmony_cistatic ssize_t show_speed(struct cpufreq_policy *policy, char *buf) 493d0407baSopenharmony_ci{ 503d0407baSopenharmony_ci return sprintf(buf, "%u\n", policy->cur); 513d0407baSopenharmony_ci} 523d0407baSopenharmony_ci 533d0407baSopenharmony_cistatic int cpufreq_userspace_policy_init(struct cpufreq_policy *policy) 543d0407baSopenharmony_ci{ 553d0407baSopenharmony_ci unsigned int *setspeed; 563d0407baSopenharmony_ci 573d0407baSopenharmony_ci setspeed = kzalloc(sizeof(*setspeed), GFP_KERNEL); 583d0407baSopenharmony_ci if (!setspeed) { 593d0407baSopenharmony_ci return -ENOMEM; 603d0407baSopenharmony_ci } 613d0407baSopenharmony_ci 623d0407baSopenharmony_ci policy->governor_data = setspeed; 633d0407baSopenharmony_ci return 0; 643d0407baSopenharmony_ci} 653d0407baSopenharmony_ci 663d0407baSopenharmony_cistatic void cpufreq_userspace_policy_exit(struct cpufreq_policy *policy) 673d0407baSopenharmony_ci{ 683d0407baSopenharmony_ci mutex_lock(&userspace_mutex); 693d0407baSopenharmony_ci kfree(policy->governor_data); 703d0407baSopenharmony_ci policy->governor_data = NULL; 713d0407baSopenharmony_ci mutex_unlock(&userspace_mutex); 723d0407baSopenharmony_ci} 733d0407baSopenharmony_ci 743d0407baSopenharmony_cistatic int cpufreq_userspace_policy_start(struct cpufreq_policy *policy) 753d0407baSopenharmony_ci{ 763d0407baSopenharmony_ci unsigned int *setspeed = policy->governor_data; 773d0407baSopenharmony_ci 783d0407baSopenharmony_ci BUG_ON(!policy->cur); 793d0407baSopenharmony_ci pr_debug("started managing cpu %u\n", policy->cpu); 803d0407baSopenharmony_ci 813d0407baSopenharmony_ci mutex_lock(&userspace_mutex); 823d0407baSopenharmony_ci per_cpu(cpu_is_managed, policy->cpu) = 1; 833d0407baSopenharmony_ci if (!*setspeed) { 843d0407baSopenharmony_ci *setspeed = policy->cur; 853d0407baSopenharmony_ci } 863d0407baSopenharmony_ci mutex_unlock(&userspace_mutex); 873d0407baSopenharmony_ci return 0; 883d0407baSopenharmony_ci} 893d0407baSopenharmony_ci 903d0407baSopenharmony_cistatic void cpufreq_userspace_policy_stop(struct cpufreq_policy *policy) 913d0407baSopenharmony_ci{ 923d0407baSopenharmony_ci pr_debug("managing cpu %u stopped\n", policy->cpu); 933d0407baSopenharmony_ci 943d0407baSopenharmony_ci mutex_lock(&userspace_mutex); 953d0407baSopenharmony_ci per_cpu(cpu_is_managed, policy->cpu) = 0; 963d0407baSopenharmony_ci mutex_unlock(&userspace_mutex); 973d0407baSopenharmony_ci} 983d0407baSopenharmony_ci 993d0407baSopenharmony_cistatic void cpufreq_userspace_policy_limits(struct cpufreq_policy *policy) 1003d0407baSopenharmony_ci{ 1013d0407baSopenharmony_ci unsigned int *setspeed = policy->governor_data; 1023d0407baSopenharmony_ci 1033d0407baSopenharmony_ci mutex_lock(&userspace_mutex); 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_ci pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz, last set to %u kHz\n", policy->cpu, policy->min, 1063d0407baSopenharmony_ci policy->max, policy->cur, *setspeed); 1073d0407baSopenharmony_ci 1083d0407baSopenharmony_ci if (policy->max < *setspeed) { 1093d0407baSopenharmony_ci __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); 1103d0407baSopenharmony_ci } else if (policy->min > *setspeed) { 1113d0407baSopenharmony_ci __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); 1123d0407baSopenharmony_ci } else { 1133d0407baSopenharmony_ci __cpufreq_driver_target(policy, *setspeed, CPUFREQ_RELATION_L); 1143d0407baSopenharmony_ci } 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_ci mutex_unlock(&userspace_mutex); 1173d0407baSopenharmony_ci} 1183d0407baSopenharmony_ci 1193d0407baSopenharmony_cistatic struct cpufreq_governor cpufreq_gov_userspace = { 1203d0407baSopenharmony_ci .name = "userspace", 1213d0407baSopenharmony_ci .init = cpufreq_userspace_policy_init, 1223d0407baSopenharmony_ci .exit = cpufreq_userspace_policy_exit, 1233d0407baSopenharmony_ci .start = cpufreq_userspace_policy_start, 1243d0407baSopenharmony_ci .stop = cpufreq_userspace_policy_stop, 1253d0407baSopenharmony_ci .limits = cpufreq_userspace_policy_limits, 1263d0407baSopenharmony_ci .store_setspeed = cpufreq_set, 1273d0407baSopenharmony_ci .show_setspeed = show_speed, 1283d0407baSopenharmony_ci .owner = THIS_MODULE, 1293d0407baSopenharmony_ci}; 1303d0407baSopenharmony_ci 1313d0407baSopenharmony_ciMODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " 1323d0407baSopenharmony_ci "Russell King <rmk@arm.linux.org.uk>"); 1333d0407baSopenharmony_ciMODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); 1343d0407baSopenharmony_ciMODULE_LICENSE("GPL"); 1353d0407baSopenharmony_ci 1363d0407baSopenharmony_ci#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 1373d0407baSopenharmony_cistruct cpufreq_governor *cpufreq_default_governor(void) 1383d0407baSopenharmony_ci{ 1393d0407baSopenharmony_ci return &cpufreq_gov_userspace; 1403d0407baSopenharmony_ci} 1413d0407baSopenharmony_ci#endif 1423d0407baSopenharmony_ci 1433d0407baSopenharmony_cicpufreq_governor_init(cpufreq_gov_userspace); 1443d0407baSopenharmony_cicpufreq_governor_exit(cpufreq_gov_userspace); 145