18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * linux/drivers/cpufreq/cpufreq_userspace.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2001 Russell King 78c2ecf20Sopenharmony_ci * (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, cpu_is_managed); 198c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(userspace_mutex); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/** 228c2ecf20Sopenharmony_ci * cpufreq_set - set the CPU frequency 238c2ecf20Sopenharmony_ci * @policy: pointer to policy struct where freq is being set 248c2ecf20Sopenharmony_ci * @freq: target frequency in kHz 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Sets the CPU frequency to freq. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int ret = -EINVAL; 318c2ecf20Sopenharmony_ci unsigned int *setspeed = policy->governor_data; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci mutex_lock(&userspace_mutex); 368c2ecf20Sopenharmony_ci if (!per_cpu(cpu_is_managed, policy->cpu)) 378c2ecf20Sopenharmony_ci goto err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci *setspeed = freq; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); 428c2ecf20Sopenharmony_ci err: 438c2ecf20Sopenharmony_ci mutex_unlock(&userspace_mutex); 448c2ecf20Sopenharmony_ci return ret; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic ssize_t show_speed(struct cpufreq_policy *policy, char *buf) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", policy->cur); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int cpufreq_userspace_policy_init(struct cpufreq_policy *policy) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned int *setspeed; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci setspeed = kzalloc(sizeof(*setspeed), GFP_KERNEL); 578c2ecf20Sopenharmony_ci if (!setspeed) 588c2ecf20Sopenharmony_ci return -ENOMEM; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci policy->governor_data = setspeed; 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void cpufreq_userspace_policy_exit(struct cpufreq_policy *policy) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci mutex_lock(&userspace_mutex); 678c2ecf20Sopenharmony_ci kfree(policy->governor_data); 688c2ecf20Sopenharmony_ci policy->governor_data = NULL; 698c2ecf20Sopenharmony_ci mutex_unlock(&userspace_mutex); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int cpufreq_userspace_policy_start(struct cpufreq_policy *policy) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned int *setspeed = policy->governor_data; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci BUG_ON(!policy->cur); 778c2ecf20Sopenharmony_ci pr_debug("started managing cpu %u\n", policy->cpu); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci mutex_lock(&userspace_mutex); 808c2ecf20Sopenharmony_ci per_cpu(cpu_is_managed, policy->cpu) = 1; 818c2ecf20Sopenharmony_ci *setspeed = policy->cur; 828c2ecf20Sopenharmony_ci mutex_unlock(&userspace_mutex); 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void cpufreq_userspace_policy_stop(struct cpufreq_policy *policy) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned int *setspeed = policy->governor_data; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci pr_debug("managing cpu %u stopped\n", policy->cpu); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mutex_lock(&userspace_mutex); 938c2ecf20Sopenharmony_ci per_cpu(cpu_is_managed, policy->cpu) = 0; 948c2ecf20Sopenharmony_ci *setspeed = 0; 958c2ecf20Sopenharmony_ci mutex_unlock(&userspace_mutex); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void cpufreq_userspace_policy_limits(struct cpufreq_policy *policy) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned int *setspeed = policy->governor_data; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci mutex_lock(&userspace_mutex); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz, last set to %u kHz\n", 1058c2ecf20Sopenharmony_ci policy->cpu, policy->min, policy->max, policy->cur, *setspeed); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (policy->max < *setspeed) 1088c2ecf20Sopenharmony_ci __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); 1098c2ecf20Sopenharmony_ci else if (policy->min > *setspeed) 1108c2ecf20Sopenharmony_ci __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); 1118c2ecf20Sopenharmony_ci else 1128c2ecf20Sopenharmony_ci __cpufreq_driver_target(policy, *setspeed, CPUFREQ_RELATION_L); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci mutex_unlock(&userspace_mutex); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct cpufreq_governor cpufreq_gov_userspace = { 1188c2ecf20Sopenharmony_ci .name = "userspace", 1198c2ecf20Sopenharmony_ci .init = cpufreq_userspace_policy_init, 1208c2ecf20Sopenharmony_ci .exit = cpufreq_userspace_policy_exit, 1218c2ecf20Sopenharmony_ci .start = cpufreq_userspace_policy_start, 1228c2ecf20Sopenharmony_ci .stop = cpufreq_userspace_policy_stop, 1238c2ecf20Sopenharmony_ci .limits = cpufreq_userspace_policy_limits, 1248c2ecf20Sopenharmony_ci .store_setspeed = cpufreq_set, 1258c2ecf20Sopenharmony_ci .show_setspeed = show_speed, 1268c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " 1308c2ecf20Sopenharmony_ci "Russell King <rmk@arm.linux.org.uk>"); 1318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); 1328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 1358c2ecf20Sopenharmony_cistruct cpufreq_governor *cpufreq_default_governor(void) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return &cpufreq_gov_userspace; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci#endif 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cicpufreq_governor_init(cpufreq_gov_userspace); 1428c2ecf20Sopenharmony_cicpufreq_governor_exit(cpufreq_gov_userspace); 143