Skip to content

Commit f525a67

Browse files
gclementvireshk
authored andcommitted
cpufreq: ap806: add cpufreq driver for Armada 8K
Add cpufreq driver for Marvell AP-806 found on Aramda 8K. The AP-806 has DFS (Dynamic Frequency Scaling) with coupled clock domain for two clusters, so this driver will directly use generic cpufreq-dt driver as backend. Based on the work of Omri Itach <omrii@marvell.com>. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
1 parent 8e3151d commit f525a67

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

drivers/cpufreq/Kconfig.arm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ config ARM_ARMADA_37XX_CPUFREQ
2525
This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
2626
The Armada 37xx PMU supports 4 frequency and VDD levels.
2727

28+
config ARM_ARMADA_8K_CPUFREQ
29+
tristate "Armada 8K CPUFreq driver"
30+
depends on ARCH_MVEBU && CPUFREQ_DT
31+
help
32+
This enables the CPUFreq driver support for Marvell
33+
Armada8k SOCs.
34+
Armada8K device has the AP806 which supports scaling
35+
to any full integer divider.
36+
37+
If in doubt, say N.
38+
2839
# big LITTLE core layer and glue drivers
2940
config ARM_BIG_LITTLE_CPUFREQ
3041
tristate "Generic ARM big LITTLE CPUfreq driver"

drivers/cpufreq/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o
5050
obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
5151

5252
obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
53+
obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o
5354
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
5455
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
5556
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o

drivers/cpufreq/armada-8k-cpufreq.c

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* CPUFreq support for Armada 8K
4+
*
5+
* Copyright (C) 2018 Marvell
6+
*
7+
* Omri Itach <omrii@marvell.com>
8+
* Gregory Clement <gregory.clement@bootlin.com>
9+
*/
10+
11+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12+
13+
#include <linux/clk.h>
14+
#include <linux/cpu.h>
15+
#include <linux/err.h>
16+
#include <linux/init.h>
17+
#include <linux/kernel.h>
18+
#include <linux/module.h>
19+
#include <linux/of.h>
20+
#include <linux/platform_device.h>
21+
#include <linux/pm_opp.h>
22+
#include <linux/slab.h>
23+
24+
/*
25+
* Setup the opps list with the divider for the max frequency, that
26+
* will be filled at runtime.
27+
*/
28+
static const int opps_div[] __initconst = {1, 2, 3, 4};
29+
30+
static struct platform_device *armada_8k_pdev;
31+
32+
struct freq_table {
33+
struct device *cpu_dev;
34+
unsigned int freq[ARRAY_SIZE(opps_div)];
35+
};
36+
37+
/* If the CPUs share the same clock, then they are in the same cluster. */
38+
static void __init armada_8k_get_sharing_cpus(struct clk *cur_clk,
39+
struct cpumask *cpumask)
40+
{
41+
int cpu;
42+
43+
for_each_possible_cpu(cpu) {
44+
struct device *cpu_dev;
45+
struct clk *clk;
46+
47+
cpu_dev = get_cpu_device(cpu);
48+
if (!cpu_dev) {
49+
pr_warn("Failed to get cpu%d device\n", cpu);
50+
continue;
51+
}
52+
53+
clk = clk_get(cpu_dev, 0);
54+
if (IS_ERR(clk)) {
55+
pr_warn("Cannot get clock for CPU %d\n", cpu);
56+
} else {
57+
if (clk_is_match(clk, cur_clk))
58+
cpumask_set_cpu(cpu, cpumask);
59+
60+
clk_put(clk);
61+
}
62+
}
63+
}
64+
65+
static int __init armada_8k_add_opp(struct clk *clk, struct device *cpu_dev,
66+
struct freq_table *freq_tables,
67+
int opps_index)
68+
{
69+
unsigned int cur_frequency;
70+
unsigned int freq;
71+
int i, ret;
72+
73+
/* Get nominal (current) CPU frequency. */
74+
cur_frequency = clk_get_rate(clk);
75+
if (!cur_frequency) {
76+
dev_err(cpu_dev, "Failed to get clock rate for this CPU\n");
77+
return -EINVAL;
78+
}
79+
80+
freq_tables[opps_index].cpu_dev = cpu_dev;
81+
82+
for (i = 0; i < ARRAY_SIZE(opps_div); i++) {
83+
freq = cur_frequency / opps_div[i];
84+
85+
ret = dev_pm_opp_add(cpu_dev, freq, 0);
86+
if (ret)
87+
return ret;
88+
89+
freq_tables[opps_index].freq[i] = freq;
90+
}
91+
92+
return 0;
93+
}
94+
95+
static void armada_8k_cpufreq_free_table(struct freq_table *freq_tables)
96+
{
97+
int opps_index, nb_cpus = num_possible_cpus();
98+
99+
for (opps_index = 0 ; opps_index <= nb_cpus; opps_index++) {
100+
int i;
101+
102+
/* If cpu_dev is NULL then we reached the end of the array */
103+
if (!freq_tables[opps_index].cpu_dev)
104+
break;
105+
106+
for (i = 0; i < ARRAY_SIZE(opps_div); i++) {
107+
/*
108+
* A 0Hz frequency is not valid, this meant
109+
* that it was not yet initialized so there is
110+
* no more opp to free
111+
*/
112+
if (freq_tables[opps_index].freq[i] == 0)
113+
break;
114+
115+
dev_pm_opp_remove(freq_tables[opps_index].cpu_dev,
116+
freq_tables[opps_index].freq[i]);
117+
}
118+
}
119+
120+
kfree(freq_tables);
121+
}
122+
123+
static int __init armada_8k_cpufreq_init(void)
124+
{
125+
int ret = 0, opps_index = 0, cpu, nb_cpus;
126+
struct freq_table *freq_tables;
127+
struct device_node *node;
128+
struct cpumask cpus;
129+
130+
node = of_find_compatible_node(NULL, NULL, "marvell,ap806-cpu-clock");
131+
if (!node || !of_device_is_available(node))
132+
return -ENODEV;
133+
134+
nb_cpus = num_possible_cpus();
135+
freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL);
136+
cpumask_copy(&cpus, cpu_possible_mask);
137+
138+
/*
139+
* For each CPU, this loop registers the operating points
140+
* supported (which are the nominal CPU frequency and full integer
141+
* divisions of it).
142+
*/
143+
for_each_cpu(cpu, &cpus) {
144+
struct cpumask shared_cpus;
145+
struct device *cpu_dev;
146+
struct clk *clk;
147+
148+
cpu_dev = get_cpu_device(cpu);
149+
150+
if (!cpu_dev) {
151+
pr_err("Cannot get CPU %d\n", cpu);
152+
continue;
153+
}
154+
155+
clk = clk_get(cpu_dev, 0);
156+
157+
if (IS_ERR(clk)) {
158+
pr_err("Cannot get clock for CPU %d\n", cpu);
159+
ret = PTR_ERR(clk);
160+
goto remove_opp;
161+
}
162+
163+
ret = armada_8k_add_opp(clk, cpu_dev, freq_tables, opps_index);
164+
if (ret) {
165+
clk_put(clk);
166+
goto remove_opp;
167+
}
168+
169+
opps_index++;
170+
cpumask_clear(&shared_cpus);
171+
armada_8k_get_sharing_cpus(clk, &shared_cpus);
172+
dev_pm_opp_set_sharing_cpus(cpu_dev, &shared_cpus);
173+
cpumask_andnot(&cpus, &cpus, &shared_cpus);
174+
clk_put(clk);
175+
}
176+
177+
armada_8k_pdev = platform_device_register_simple("cpufreq-dt", -1,
178+
NULL, 0);
179+
ret = PTR_ERR_OR_ZERO(armada_8k_pdev);
180+
if (ret)
181+
goto remove_opp;
182+
183+
platform_set_drvdata(armada_8k_pdev, freq_tables);
184+
185+
return 0;
186+
187+
remove_opp:
188+
armada_8k_cpufreq_free_table(freq_tables);
189+
return ret;
190+
}
191+
module_init(armada_8k_cpufreq_init);
192+
193+
static void __exit armada_8k_cpufreq_exit(void)
194+
{
195+
struct freq_table *freq_tables = platform_get_drvdata(armada_8k_pdev);
196+
197+
platform_device_unregister(armada_8k_pdev);
198+
armada_8k_cpufreq_free_table(freq_tables);
199+
}
200+
module_exit(armada_8k_cpufreq_exit);
201+
202+
MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
203+
MODULE_DESCRIPTION("Armada 8K cpufreq driver");
204+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)