bmips-cpufreq.c (4377B)
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 33enum bmips_type { 34 BMIPS5000, 35 BMIPS5200, 36}; 37 38struct 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 52static struct cpufreq_compat bmips_cpufreq_compat[] = { 53 BMIPS("brcm,bmips5000", BMIPS5000, 8, 4), 54 BMIPS("brcm,bmips5200", BMIPS5200, 8, 4), 55 { } 56}; 57 58static struct cpufreq_compat *priv; 59 60static int htp_freq_to_cpu_freq(unsigned int clk_mult) 61{ 62 return mips_hpt_frequency * clk_mult / 1000; 63} 64 65static struct cpufreq_frequency_table * 66bmips_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 87static 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 105static 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 124static int bmips_cpufreq_exit(struct cpufreq_policy *policy) 125{ 126 kfree(policy->freq_table); 127 128 return 0; 129} 130 131static int bmips_cpufreq_init(struct cpufreq_policy *policy) 132{ 133 struct cpufreq_frequency_table *freq_table; 134 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); 140 } 141 142 cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY); 143 pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME); 144 145 return 0; 146} 147 148static 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, 157}; 158 159static int __init bmips_cpufreq_probe(void) 160{ 161 struct cpufreq_compat *cc; 162 struct device_node *np; 163 164 for (cc = bmips_cpufreq_compat; cc->compatible; cc++) { 165 np = of_find_compatible_node(NULL, "cpu", cc->compatible); 166 if (np) { 167 of_node_put(np); 168 priv = cc; 169 break; 170 } 171 } 172 173 /* We hit the guard element of the array. No compatible CPU found. */ 174 if (!cc->compatible) 175 return -ENODEV; 176 177 return cpufreq_register_driver(&bmips_cpufreq_driver); 178} 179device_initcall(bmips_cpufreq_probe); 180 181MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>"); 182MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs"); 183MODULE_LICENSE("GPL");