Skip to content

Commit 72c38ec

Browse files
Nick HuCharlesWu465
authored andcommitted
riscv: Power Brake
This is the commit for Power brake. In Andes extension, Power brake register divide cpu frequency into 16 parts, you can set the level you want to scale the cpu frequency(reference AndeStar V5 SPA). Also, the users can set value in '/${Path to cpufreq}/ scaling_max/min_freq' to decide the range you want to scale cpu frequency. The cpufreq driver will help you to set the level in Power brake regsiter. Signed-off-by: Nick Hu <nickhu@andestech.com>
1 parent c2d003e commit 72c38ec

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

arch/riscv/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ config ARCH_MMAP_RND_BITS_MIN
7676
config ARCH_MMAP_RND_BITS_MAX
7777
default 24 if 64BIT # SV39 based
7878
default 17
79+
select MODULE_SECTIONS if MODULES
80+
config CPU_FREQ
81+
def_bool y
82+
83+
config RISCV_CPUFREQ
84+
def_bool y
85+
86+
config CPU_FREQ_GOV_POWERSAVE
87+
def_bool y
88+
89+
config CPU_FREQ_DEFAULT_GOV_POWERSAVE
90+
def_bool y
7991

8092
config MMU
8193
def_bool y

arch/riscv/andesv5/sbi.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ void sbi_restart(int cpu_num)
2828
}
2929
EXPORT_SYMBOL(sbi_restart);
3030

31+
void sbi_write_powerbrake(int val)
32+
{
33+
sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_POWERBRAKE, val, 0, 0, 0, 0, 0);
34+
}
35+
EXPORT_SYMBOL(sbi_write_powerbrake);
36+
37+
int sbi_read_powerbrake(void)
38+
{
39+
struct sbiret ret;
40+
ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_READ_POWERBRAKE, 0, 0, 0, 0, 0, 0);
41+
return ret.value;
42+
}
43+
EXPORT_SYMBOL(sbi_read_powerbrake);
44+
3145
void sbi_set_suspend_mode(int suspend_mode)
3246
{
3347
sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_SUSPEND_MODE, suspend_mode, 0, 0, 0, 0, 0);

arch/riscv/include/asm/sbi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ void sbi_set_reset_vec(int val);
182182
void sbi_set_pma(phys_addr_t offset, unsigned long vaddr, size_t size);
183183
void sbi_free_pma(unsigned long vaddr);
184184
long sbi_probe_pma(void);
185+
void sbi_write_powerbrake(int val);
186+
int sbi_read_powerbrake(void);
185187

186188
#else /* CONFIG_RISCV_SBI */
187189
/* stubs for code that is only reachable under IS_ENABLED(CONFIG_RISCV_SBI): */

drivers/cpufreq/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
101101
obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o
102102
obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o
103103

104+
##################################################################################
105+
# RISCV platform drivers
106+
obj-$(CONFIG_RISCV_CPUFREQ) += riscv-cpufreq.o
107+
104108
##################################################################################
105109
# Other platform drivers
106110
obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o

drivers/cpufreq/riscv-cpufreq.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#include <linux/kernel.h>
2+
#include <linux/module.h>
3+
#include <linux/init.h>
4+
#include <linux/cpufreq.h>
5+
#include <linux/timex.h>
6+
7+
#include <asm/sbi.h>
8+
9+
static unsigned int riscv_cpufreq_get(unsigned int cpu)
10+
{
11+
int val;
12+
13+
if (cpu)
14+
return 0;
15+
16+
val = sbi_read_powerbrake();
17+
val = val & 0xf0;
18+
val = val >> 4;
19+
20+
if (val == 0)
21+
return 60 * 1024;
22+
else if (val == 15)
23+
return 10 * 1024;
24+
25+
return (52 - val * 3 + 2) * 1024;
26+
}
27+
28+
void write_powerbrake(void *arg)
29+
{
30+
int *val = arg;
31+
32+
sbi_write_powerbrake(*val);
33+
}
34+
35+
static int riscv_cpufreq_set_policy(struct cpufreq_policy *policy)
36+
{
37+
int val, i;
38+
unsigned int cpu = policy->cpu;
39+
40+
if (!policy)
41+
return -EINVAL;
42+
43+
val = (policy->min + policy->max) / 2;
44+
switch (policy->policy) {
45+
case CPUFREQ_POLICY_POWERSAVE:
46+
val = (val + policy->min) / 2 / 1024;
47+
break;
48+
case CPUFREQ_POLICY_PERFORMANCE:
49+
val = (val + policy->max) / 2 / 1024;
50+
break;
51+
default:
52+
pr_err ("Not Support this governor\n");
53+
break;
54+
}
55+
56+
if (val < 10)
57+
val = 15;
58+
else if (val > 51)
59+
val = 0;
60+
else {
61+
// search for level
62+
for (i = 1; i < 15 ; i++) {
63+
if ((val >= 52 - i * 3) &&
64+
(val <= 52 - i * 3 + 2)) {
65+
val = i;
66+
break;
67+
}
68+
}
69+
}
70+
71+
val = val << 4;
72+
return smp_call_function_single(cpu, write_powerbrake, &val, 1);
73+
}
74+
75+
static int riscv_cpufreq_verify_policy(struct cpufreq_policy *policy)
76+
{
77+
if (!policy)
78+
return -EINVAL;
79+
80+
cpufreq_verify_within_cpu_limits(policy);
81+
82+
if((policy->policy != CPUFREQ_POLICY_POWERSAVE) &&
83+
(policy->policy != CPUFREQ_POLICY_PERFORMANCE))
84+
return -EINVAL;
85+
86+
return 0;
87+
}
88+
89+
static void riscv_get_policy(struct cpufreq_policy *policy)
90+
{
91+
int val;
92+
93+
val = sbi_read_powerbrake();
94+
val = val & 0xf0;
95+
val = val >> 4;
96+
/*
97+
* Powerbrake register has 16 level,
98+
* so we divide the 60Mhz into 16 parts.
99+
* | | |........| | |
100+
* Mhz 0 10 13 49 52 60
101+
*/
102+
if (val == 0) { // highest performance
103+
policy->min = 52 * 1024;
104+
policy->max = 60 * 1024;
105+
} else if (val == 15) { // lowest performance
106+
policy->min = 0;
107+
policy->max = 10 * 1024;
108+
} else {
109+
policy->min = 52 - val * 3;
110+
policy->max = 52 - val * 3 + 2;
111+
}
112+
113+
policy->policy = CPUFREQ_POLICY_POWERSAVE;
114+
}
115+
116+
static int riscv_cpufreq_cpu_init(struct cpufreq_policy *policy)
117+
{
118+
119+
policy->cpuinfo.min_freq = 0;
120+
policy->cpuinfo.max_freq = 60*1024; /* 60Mhz=60*1024khz */
121+
122+
riscv_get_policy(policy);
123+
124+
return 0;
125+
}
126+
127+
static struct cpufreq_driver riscv_cpufreq_driver = {
128+
.flags = CPUFREQ_CONST_LOOPS,
129+
.verify = riscv_cpufreq_verify_policy,
130+
.setpolicy = riscv_cpufreq_set_policy,
131+
.get = riscv_cpufreq_get,
132+
.init = riscv_cpufreq_cpu_init,
133+
.name = "riscv_cpufreq",
134+
};
135+
136+
static int __init riscv_cpufreq_init(void)
137+
{
138+
return cpufreq_register_driver(&riscv_cpufreq_driver);
139+
}
140+
141+
static void __exit riscv_cpufreq_exit(void)
142+
{
143+
cpufreq_unregister_driver(&riscv_cpufreq_driver);
144+
}
145+
146+
MODULE_AUTHOR("Nick Hu <nickhu@andestech.com>");
147+
MODULE_DESCRIPTION("Riscv cpufreq driver.");
148+
MODULE_LICENSE("GPL");
149+
module_init(riscv_cpufreq_init);
150+
module_exit(riscv_cpufreq_exit);

0 commit comments

Comments
 (0)