123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- /*
- * CPU frequency scaling for Broadcom BMIPS SoCs
- *
- * Copyright (c) 2017 Broadcom
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/cpufreq.h>
- #include <linux/module.h>
- #include <linux/of_address.h>
- #include <linux/slab.h>
- /* for mips_hpt_frequency */
- #include <asm/time.h>
- #define BMIPS_CPUFREQ_PREFIX "bmips"
- #define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
- #define TRANSITION_LATENCY (25 * 1000) /* 25 us */
- #define BMIPS5_CLK_DIV_SET_SHIFT 0x7
- #define BMIPS5_CLK_DIV_SHIFT 0x4
- #define BMIPS5_CLK_DIV_MASK 0xf
- enum bmips_type {
- BMIPS5000,
- BMIPS5200,
- };
- struct cpufreq_compat {
- const char *compatible;
- unsigned int bmips_type;
- unsigned int clk_mult;
- unsigned int max_freqs;
- };
- #define BMIPS(c, t, m, f) { \
- .compatible = c, \
- .bmips_type = (t), \
- .clk_mult = (m), \
- .max_freqs = (f), \
- }
- static struct cpufreq_compat bmips_cpufreq_compat[] = {
- BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
- BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
- { }
- };
- static struct cpufreq_compat *priv;
- static int htp_freq_to_cpu_freq(unsigned int clk_mult)
- {
- return mips_hpt_frequency * clk_mult / 1000;
- }
- static struct cpufreq_frequency_table *
- bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
- {
- struct cpufreq_frequency_table *table;
- unsigned long cpu_freq;
- int i;
- cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
- table = kmalloc_array(priv->max_freqs + 1, sizeof(*table), GFP_KERNEL);
- if (!table)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < priv->max_freqs; i++) {
- table[i].frequency = cpu_freq / (1 << i);
- table[i].driver_data = i;
- }
- table[i].frequency = CPUFREQ_TABLE_END;
- return table;
- }
- static unsigned int bmips_cpufreq_get(unsigned int cpu)
- {
- unsigned int div;
- uint32_t mode;
- switch (priv->bmips_type) {
- case BMIPS5200:
- case BMIPS5000:
- mode = read_c0_brcm_mode();
- div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
- break;
- default:
- div = 0;
- }
- return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
- }
- static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
- unsigned int index)
- {
- unsigned int div = policy->freq_table[index].driver_data;
- switch (priv->bmips_type) {
- case BMIPS5200:
- case BMIPS5000:
- change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
- (1 << BMIPS5_CLK_DIV_SET_SHIFT) |
- (div << BMIPS5_CLK_DIV_SHIFT));
- break;
- default:
- return -ENOTSUPP;
- }
- return 0;
- }
- static int bmips_cpufreq_exit(struct cpufreq_policy *policy)
- {
- kfree(policy->freq_table);
- return 0;
- }
- static int bmips_cpufreq_init(struct cpufreq_policy *policy)
- {
- struct cpufreq_frequency_table *freq_table;
- int ret;
- freq_table = bmips_cpufreq_get_freq_table(policy);
- if (IS_ERR(freq_table)) {
- ret = PTR_ERR(freq_table);
- pr_err("%s: couldn't determine frequency table (%d).\n",
- BMIPS_CPUFREQ_NAME, ret);
- return ret;
- }
- ret = cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
- if (ret)
- bmips_cpufreq_exit(policy);
- else
- pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
- return ret;
- }
- static struct cpufreq_driver bmips_cpufreq_driver = {
- .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = bmips_cpufreq_target_index,
- .get = bmips_cpufreq_get,
- .init = bmips_cpufreq_init,
- .exit = bmips_cpufreq_exit,
- .attr = cpufreq_generic_attr,
- .name = BMIPS_CPUFREQ_PREFIX,
- };
- static int __init bmips_cpufreq_probe(void)
- {
- struct cpufreq_compat *cc;
- struct device_node *np;
- for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
- np = of_find_compatible_node(NULL, "cpu", cc->compatible);
- if (np) {
- of_node_put(np);
- priv = cc;
- break;
- }
- }
- /* We hit the guard element of the array. No compatible CPU found. */
- if (!cc->compatible)
- return -ENODEV;
- return cpufreq_register_driver(&bmips_cpufreq_driver);
- }
- device_initcall(bmips_cpufreq_probe);
- MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
- MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
- MODULE_LICENSE("GPL");
|