Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpufreq: bmips-cpufreq: CPUfreq driver for Broadcom's BMIPS SoCs

Add the MIPS CPUfreq driver. This driver currently supports CPUfreq on
BMIPS5xxx-based SoCs.

Signed-off-by: Markus Mayer <mmayer@broadcom.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Markus Mayer and committed by
Rafael J. Wysocki
cdb56cbf a8d709b0

+199
+10
drivers/cpufreq/Kconfig
··· 263 263 endif 264 264 265 265 if MIPS 266 + config BMIPS_CPUFREQ 267 + tristate "BMIPS CPUfreq Driver" 268 + help 269 + This option adds a CPUfreq driver for BMIPS processors with 270 + support for configurable CPU frequency. 271 + 272 + For now, BMIPS5 chips are supported (such as the Broadcom 7425). 273 + 274 + If in doubt, say N. 275 + 266 276 config LOONGSON2_CPUFREQ 267 277 tristate "Loongson2 CPUFreq Driver" 268 278 help
+1
drivers/cpufreq/Makefile
··· 98 98 # Other platform drivers 99 99 obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o 100 100 obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o 101 + obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o 101 102 obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o 102 103 obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o 103 104 obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
+188
drivers/cpufreq/bmips-cpufreq.c
··· 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((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 int bmips_cpufreq_exit(struct cpufreq_policy *policy) 125 + { 126 + kfree(policy->freq_table); 127 + 128 + return 0; 129 + } 130 + 131 + static int bmips_cpufreq_init(struct cpufreq_policy *policy) 132 + { 133 + struct cpufreq_frequency_table *freq_table; 134 + int ret; 135 + 136 + freq_table = bmips_cpufreq_get_freq_table(policy); 137 + if (IS_ERR(freq_table)) { 138 + ret = PTR_ERR(freq_table); 139 + pr_err("%s: couldn't determine frequency table (%d).\n", 140 + BMIPS_CPUFREQ_NAME, ret); 141 + return ret; 142 + } 143 + 144 + ret = cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY); 145 + if (ret) 146 + bmips_cpufreq_exit(policy); 147 + else 148 + pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME); 149 + 150 + return ret; 151 + } 152 + 153 + static struct cpufreq_driver bmips_cpufreq_driver = { 154 + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 155 + .verify = cpufreq_generic_frequency_table_verify, 156 + .target_index = bmips_cpufreq_target_index, 157 + .get = bmips_cpufreq_get, 158 + .init = bmips_cpufreq_init, 159 + .exit = bmips_cpufreq_exit, 160 + .attr = cpufreq_generic_attr, 161 + .name = BMIPS_CPUFREQ_PREFIX, 162 + }; 163 + 164 + static int __init bmips_cpufreq_probe(void) 165 + { 166 + struct cpufreq_compat *cc; 167 + struct device_node *np; 168 + 169 + for (cc = bmips_cpufreq_compat; cc->compatible; cc++) { 170 + np = of_find_compatible_node(NULL, "cpu", cc->compatible); 171 + if (np) { 172 + of_node_put(np); 173 + priv = cc; 174 + break; 175 + } 176 + } 177 + 178 + /* We hit the guard element of the array. No compatible CPU found. */ 179 + if (!cc->compatible) 180 + return -ENODEV; 181 + 182 + return cpufreq_register_driver(&bmips_cpufreq_driver); 183 + } 184 + device_initcall(bmips_cpufreq_probe); 185 + 186 + MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>"); 187 + MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs"); 188 + MODULE_LICENSE("GPL");