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