Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/cpufreq/bmips-cpufreq.c
26278 views
1
/*
2
* CPU frequency scaling for Broadcom BMIPS SoCs
3
*
4
* Copyright (c) 2017 Broadcom
5
*
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License as
8
* published by the Free Software Foundation version 2.
9
*
10
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
11
* kind, whether express or implied; without even the implied warranty
12
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
*/
15
16
#include <linux/cpufreq.h>
17
#include <linux/module.h>
18
#include <linux/of_address.h>
19
#include <linux/slab.h>
20
21
/* for mips_hpt_frequency */
22
#include <asm/time.h>
23
24
#define BMIPS_CPUFREQ_PREFIX "bmips"
25
#define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
26
27
#define TRANSITION_LATENCY (25 * 1000) /* 25 us */
28
29
#define BMIPS5_CLK_DIV_SET_SHIFT 0x7
30
#define BMIPS5_CLK_DIV_SHIFT 0x4
31
#define BMIPS5_CLK_DIV_MASK 0xf
32
33
enum bmips_type {
34
BMIPS5000,
35
BMIPS5200,
36
};
37
38
struct cpufreq_compat {
39
const char *compatible;
40
unsigned int bmips_type;
41
unsigned int clk_mult;
42
unsigned int max_freqs;
43
};
44
45
#define BMIPS(c, t, m, f) { \
46
.compatible = c, \
47
.bmips_type = (t), \
48
.clk_mult = (m), \
49
.max_freqs = (f), \
50
}
51
52
static struct cpufreq_compat bmips_cpufreq_compat[] = {
53
BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
54
BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
55
{ }
56
};
57
58
static struct cpufreq_compat *priv;
59
60
static int htp_freq_to_cpu_freq(unsigned int clk_mult)
61
{
62
return mips_hpt_frequency * clk_mult / 1000;
63
}
64
65
static struct cpufreq_frequency_table *
66
bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
67
{
68
struct cpufreq_frequency_table *table;
69
unsigned long cpu_freq;
70
int i;
71
72
cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
73
74
table = kmalloc_array(priv->max_freqs + 1, sizeof(*table), GFP_KERNEL);
75
if (!table)
76
return ERR_PTR(-ENOMEM);
77
78
for (i = 0; i < priv->max_freqs; i++) {
79
table[i].frequency = cpu_freq / (1 << i);
80
table[i].driver_data = i;
81
}
82
table[i].frequency = CPUFREQ_TABLE_END;
83
84
return table;
85
}
86
87
static unsigned int bmips_cpufreq_get(unsigned int cpu)
88
{
89
unsigned int div;
90
uint32_t mode;
91
92
switch (priv->bmips_type) {
93
case BMIPS5200:
94
case BMIPS5000:
95
mode = read_c0_brcm_mode();
96
div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
97
break;
98
default:
99
div = 0;
100
}
101
102
return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
103
}
104
105
static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
106
unsigned int index)
107
{
108
unsigned int div = policy->freq_table[index].driver_data;
109
110
switch (priv->bmips_type) {
111
case BMIPS5200:
112
case BMIPS5000:
113
change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
114
(1 << BMIPS5_CLK_DIV_SET_SHIFT) |
115
(div << BMIPS5_CLK_DIV_SHIFT));
116
break;
117
default:
118
return -ENOTSUPP;
119
}
120
121
return 0;
122
}
123
124
static void bmips_cpufreq_exit(struct cpufreq_policy *policy)
125
{
126
kfree(policy->freq_table);
127
}
128
129
static int bmips_cpufreq_init(struct cpufreq_policy *policy)
130
{
131
struct cpufreq_frequency_table *freq_table;
132
133
freq_table = bmips_cpufreq_get_freq_table(policy);
134
if (IS_ERR(freq_table)) {
135
pr_err("%s: couldn't determine frequency table (%ld).\n",
136
BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
137
return PTR_ERR(freq_table);
138
}
139
140
cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
141
pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
142
143
return 0;
144
}
145
146
static struct cpufreq_driver bmips_cpufreq_driver = {
147
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
148
.verify = cpufreq_generic_frequency_table_verify,
149
.target_index = bmips_cpufreq_target_index,
150
.get = bmips_cpufreq_get,
151
.init = bmips_cpufreq_init,
152
.exit = bmips_cpufreq_exit,
153
.name = BMIPS_CPUFREQ_PREFIX,
154
};
155
156
static int __init bmips_cpufreq_driver_init(void)
157
{
158
struct cpufreq_compat *cc;
159
struct device_node *np;
160
161
for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
162
np = of_find_compatible_node(NULL, "cpu", cc->compatible);
163
if (np) {
164
of_node_put(np);
165
priv = cc;
166
break;
167
}
168
}
169
170
/* We hit the guard element of the array. No compatible CPU found. */
171
if (!cc->compatible)
172
return -ENODEV;
173
174
return cpufreq_register_driver(&bmips_cpufreq_driver);
175
}
176
module_init(bmips_cpufreq_driver_init);
177
178
static void __exit bmips_cpufreq_driver_exit(void)
179
{
180
cpufreq_unregister_driver(&bmips_cpufreq_driver);
181
}
182
module_exit(bmips_cpufreq_driver_exit);
183
184
MODULE_AUTHOR("Markus Mayer <[email protected]>");
185
MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
186
MODULE_LICENSE("GPL");
187
188