1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | /* |
4 | * linux/drivers/cpufreq/cpufreq_userspace.c |
5 | * |
6 | * Copyright (C) 2001 Russell King |
7 | * (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/cpufreq.h> |
13 | #include <linux/init.h> |
14 | #include <linux/module.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/slab.h> |
17 | |
18 | struct userspace_policy { |
19 | unsigned int is_managed; |
20 | unsigned int setspeed; |
21 | struct mutex mutex; |
22 | }; |
23 | |
24 | /** |
25 | * cpufreq_set - set the CPU frequency |
26 | * @policy: pointer to policy struct where freq is being set |
27 | * @freq: target frequency in kHz |
28 | * |
29 | * Sets the CPU frequency to freq. |
30 | */ |
31 | static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) |
32 | { |
33 | int ret = -EINVAL; |
34 | struct userspace_policy *userspace = policy->governor_data; |
35 | |
36 | pr_debug("cpufreq_set for cpu %u, freq %u kHz\n" , policy->cpu, freq); |
37 | |
38 | mutex_lock(&userspace->mutex); |
39 | if (!userspace->is_managed) |
40 | goto err; |
41 | |
42 | userspace->setspeed = freq; |
43 | |
44 | ret = __cpufreq_driver_target(policy, target_freq: freq, CPUFREQ_RELATION_L); |
45 | err: |
46 | mutex_unlock(lock: &userspace->mutex); |
47 | return ret; |
48 | } |
49 | |
50 | static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) |
51 | { |
52 | return sprintf(buf, fmt: "%u\n" , policy->cur); |
53 | } |
54 | |
55 | static int cpufreq_userspace_policy_init(struct cpufreq_policy *policy) |
56 | { |
57 | struct userspace_policy *userspace; |
58 | |
59 | userspace = kzalloc(size: sizeof(*userspace), GFP_KERNEL); |
60 | if (!userspace) |
61 | return -ENOMEM; |
62 | |
63 | mutex_init(&userspace->mutex); |
64 | |
65 | policy->governor_data = userspace; |
66 | return 0; |
67 | } |
68 | |
69 | /* |
70 | * Any routine that writes to the policy struct will hold the "rwsem" of |
71 | * policy struct that means it is free to free "governor_data" here. |
72 | */ |
73 | static void cpufreq_userspace_policy_exit(struct cpufreq_policy *policy) |
74 | { |
75 | kfree(objp: policy->governor_data); |
76 | policy->governor_data = NULL; |
77 | } |
78 | |
79 | static int cpufreq_userspace_policy_start(struct cpufreq_policy *policy) |
80 | { |
81 | struct userspace_policy *userspace = policy->governor_data; |
82 | |
83 | BUG_ON(!policy->cur); |
84 | pr_debug("started managing cpu %u\n" , policy->cpu); |
85 | |
86 | mutex_lock(&userspace->mutex); |
87 | userspace->is_managed = 1; |
88 | userspace->setspeed = policy->cur; |
89 | mutex_unlock(lock: &userspace->mutex); |
90 | return 0; |
91 | } |
92 | |
93 | static void cpufreq_userspace_policy_stop(struct cpufreq_policy *policy) |
94 | { |
95 | struct userspace_policy *userspace = policy->governor_data; |
96 | |
97 | pr_debug("managing cpu %u stopped\n" , policy->cpu); |
98 | |
99 | mutex_lock(&userspace->mutex); |
100 | userspace->is_managed = 0; |
101 | userspace->setspeed = 0; |
102 | mutex_unlock(lock: &userspace->mutex); |
103 | } |
104 | |
105 | static void cpufreq_userspace_policy_limits(struct cpufreq_policy *policy) |
106 | { |
107 | struct userspace_policy *userspace = policy->governor_data; |
108 | |
109 | mutex_lock(&userspace->mutex); |
110 | |
111 | pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz, last set to %u kHz\n" , |
112 | policy->cpu, policy->min, policy->max, policy->cur, userspace->setspeed); |
113 | |
114 | if (policy->max < userspace->setspeed) |
115 | __cpufreq_driver_target(policy, target_freq: policy->max, |
116 | CPUFREQ_RELATION_H); |
117 | else if (policy->min > userspace->setspeed) |
118 | __cpufreq_driver_target(policy, target_freq: policy->min, |
119 | CPUFREQ_RELATION_L); |
120 | else |
121 | __cpufreq_driver_target(policy, target_freq: userspace->setspeed, |
122 | CPUFREQ_RELATION_L); |
123 | |
124 | mutex_unlock(lock: &userspace->mutex); |
125 | } |
126 | |
127 | static struct cpufreq_governor cpufreq_gov_userspace = { |
128 | .name = "userspace" , |
129 | .init = cpufreq_userspace_policy_init, |
130 | .exit = cpufreq_userspace_policy_exit, |
131 | .start = cpufreq_userspace_policy_start, |
132 | .stop = cpufreq_userspace_policy_stop, |
133 | .limits = cpufreq_userspace_policy_limits, |
134 | .store_setspeed = cpufreq_set, |
135 | .show_setspeed = show_speed, |
136 | .owner = THIS_MODULE, |
137 | }; |
138 | |
139 | MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " |
140 | "Russell King <rmk@arm.linux.org.uk>" ); |
141 | MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'" ); |
142 | MODULE_LICENSE("GPL" ); |
143 | |
144 | #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE |
145 | struct cpufreq_governor *cpufreq_default_governor(void) |
146 | { |
147 | return &cpufreq_gov_userspace; |
148 | } |
149 | #endif |
150 | |
151 | cpufreq_governor_init(cpufreq_gov_userspace); |
152 | cpufreq_governor_exit(cpufreq_gov_userspace); |
153 | |