2 * CPU frequency scaling for Broadcom BMIPS SoCs
4 * Copyright (c) 2017 Broadcom
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.
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.
16 #include <linux/cpufreq.h>
17 #include <linux/module.h>
18 #include <linux/of_address.h>
19 #include <linux/slab.h>
21 /* for mips_hpt_frequency */
24 #define BMIPS_CPUFREQ_PREFIX "bmips"
25 #define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
27 #define TRANSITION_LATENCY (25 * 1000) /* 25 us */
29 #define BMIPS5_CLK_DIV_SET_SHIFT 0x7
30 #define BMIPS5_CLK_DIV_SHIFT 0x4
31 #define BMIPS5_CLK_DIV_MASK 0xf
38 struct cpufreq_compat
{
39 const char *compatible
;
40 unsigned int bmips_type
;
41 unsigned int clk_mult
;
42 unsigned int max_freqs
;
45 #define BMIPS(c, t, m, f) { \
52 static struct cpufreq_compat bmips_cpufreq_compat
[] = {
53 BMIPS("brcm,bmips5000", BMIPS5000
, 8, 4),
54 BMIPS("brcm,bmips5200", BMIPS5200
, 8, 4),
58 static struct cpufreq_compat
*priv
;
60 static int htp_freq_to_cpu_freq(unsigned int clk_mult
)
62 return mips_hpt_frequency
* clk_mult
/ 1000;
65 static struct cpufreq_frequency_table
*
66 bmips_cpufreq_get_freq_table(const struct cpufreq_policy
*policy
)
68 struct cpufreq_frequency_table
*table
;
69 unsigned long cpu_freq
;
72 cpu_freq
= htp_freq_to_cpu_freq(priv
->clk_mult
);
74 table
= kmalloc_array(priv
->max_freqs
+ 1, sizeof(*table
), GFP_KERNEL
);
76 return ERR_PTR(-ENOMEM
);
78 for (i
= 0; i
< priv
->max_freqs
; i
++) {
79 table
[i
].frequency
= cpu_freq
/ (1 << i
);
80 table
[i
].driver_data
= i
;
82 table
[i
].frequency
= CPUFREQ_TABLE_END
;
87 static unsigned int bmips_cpufreq_get(unsigned int cpu
)
92 switch (priv
->bmips_type
) {
95 mode
= read_c0_brcm_mode();
96 div
= ((mode
>> BMIPS5_CLK_DIV_SHIFT
) & BMIPS5_CLK_DIV_MASK
);
102 return htp_freq_to_cpu_freq(priv
->clk_mult
) / (1 << div
);
105 static int bmips_cpufreq_target_index(struct cpufreq_policy
*policy
,
108 unsigned int div
= policy
->freq_table
[index
].driver_data
;
110 switch (priv
->bmips_type
) {
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
));
124 static int bmips_cpufreq_exit(struct cpufreq_policy
*policy
)
126 kfree(policy
->freq_table
);
131 static int bmips_cpufreq_init(struct cpufreq_policy
*policy
)
133 struct cpufreq_frequency_table
*freq_table
;
135 freq_table
= bmips_cpufreq_get_freq_table(policy
);
136 if (IS_ERR(freq_table
)) {
137 pr_err("%s: couldn't determine frequency table (%ld).\n",
138 BMIPS_CPUFREQ_NAME
, PTR_ERR(freq_table
));
139 return PTR_ERR(freq_table
);
142 cpufreq_generic_init(policy
, freq_table
, TRANSITION_LATENCY
);
143 pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME
);
148 static struct cpufreq_driver bmips_cpufreq_driver
= {
149 .flags
= CPUFREQ_NEED_INITIAL_FREQ_CHECK
,
150 .verify
= cpufreq_generic_frequency_table_verify
,
151 .target_index
= bmips_cpufreq_target_index
,
152 .get
= bmips_cpufreq_get
,
153 .init
= bmips_cpufreq_init
,
154 .exit
= bmips_cpufreq_exit
,
155 .attr
= cpufreq_generic_attr
,
156 .name
= BMIPS_CPUFREQ_PREFIX
,
159 static int __init
bmips_cpufreq_probe(void)
161 struct cpufreq_compat
*cc
;
162 struct device_node
*np
;
164 for (cc
= bmips_cpufreq_compat
; cc
->compatible
; cc
++) {
165 np
= of_find_compatible_node(NULL
, "cpu", cc
->compatible
);
173 /* We hit the guard element of the array. No compatible CPU found. */
177 return cpufreq_register_driver(&bmips_cpufreq_driver
);
179 device_initcall(bmips_cpufreq_probe
);
181 MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
182 MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
183 MODULE_LICENSE("GPL");