0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/cpufreq.h>
0017 #include <linux/module.h>
0018 #include <linux/of_address.h>
0019 #include <linux/slab.h>
0020
0021
0022 #include <asm/time.h>
0023
0024 #define BMIPS_CPUFREQ_PREFIX "bmips"
0025 #define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
0026
0027 #define TRANSITION_LATENCY (25 * 1000)
0028
0029 #define BMIPS5_CLK_DIV_SET_SHIFT 0x7
0030 #define BMIPS5_CLK_DIV_SHIFT 0x4
0031 #define BMIPS5_CLK_DIV_MASK 0xf
0032
0033 enum bmips_type {
0034 BMIPS5000,
0035 BMIPS5200,
0036 };
0037
0038 struct cpufreq_compat {
0039 const char *compatible;
0040 unsigned int bmips_type;
0041 unsigned int clk_mult;
0042 unsigned int max_freqs;
0043 };
0044
0045 #define BMIPS(c, t, m, f) { \
0046 .compatible = c, \
0047 .bmips_type = (t), \
0048 .clk_mult = (m), \
0049 .max_freqs = (f), \
0050 }
0051
0052 static struct cpufreq_compat bmips_cpufreq_compat[] = {
0053 BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
0054 BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
0055 { }
0056 };
0057
0058 static struct cpufreq_compat *priv;
0059
0060 static int htp_freq_to_cpu_freq(unsigned int clk_mult)
0061 {
0062 return mips_hpt_frequency * clk_mult / 1000;
0063 }
0064
0065 static struct cpufreq_frequency_table *
0066 bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
0067 {
0068 struct cpufreq_frequency_table *table;
0069 unsigned long cpu_freq;
0070 int i;
0071
0072 cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
0073
0074 table = kmalloc_array(priv->max_freqs + 1, sizeof(*table), GFP_KERNEL);
0075 if (!table)
0076 return ERR_PTR(-ENOMEM);
0077
0078 for (i = 0; i < priv->max_freqs; i++) {
0079 table[i].frequency = cpu_freq / (1 << i);
0080 table[i].driver_data = i;
0081 }
0082 table[i].frequency = CPUFREQ_TABLE_END;
0083
0084 return table;
0085 }
0086
0087 static unsigned int bmips_cpufreq_get(unsigned int cpu)
0088 {
0089 unsigned int div;
0090 uint32_t mode;
0091
0092 switch (priv->bmips_type) {
0093 case BMIPS5200:
0094 case BMIPS5000:
0095 mode = read_c0_brcm_mode();
0096 div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
0097 break;
0098 default:
0099 div = 0;
0100 }
0101
0102 return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
0103 }
0104
0105 static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
0106 unsigned int index)
0107 {
0108 unsigned int div = policy->freq_table[index].driver_data;
0109
0110 switch (priv->bmips_type) {
0111 case BMIPS5200:
0112 case BMIPS5000:
0113 change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
0114 (1 << BMIPS5_CLK_DIV_SET_SHIFT) |
0115 (div << BMIPS5_CLK_DIV_SHIFT));
0116 break;
0117 default:
0118 return -ENOTSUPP;
0119 }
0120
0121 return 0;
0122 }
0123
0124 static int bmips_cpufreq_exit(struct cpufreq_policy *policy)
0125 {
0126 kfree(policy->freq_table);
0127
0128 return 0;
0129 }
0130
0131 static int bmips_cpufreq_init(struct cpufreq_policy *policy)
0132 {
0133 struct cpufreq_frequency_table *freq_table;
0134
0135 freq_table = bmips_cpufreq_get_freq_table(policy);
0136 if (IS_ERR(freq_table)) {
0137 pr_err("%s: couldn't determine frequency table (%ld).\n",
0138 BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
0139 return PTR_ERR(freq_table);
0140 }
0141
0142 cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
0143 pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
0144
0145 return 0;
0146 }
0147
0148 static struct cpufreq_driver bmips_cpufreq_driver = {
0149 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
0150 .verify = cpufreq_generic_frequency_table_verify,
0151 .target_index = bmips_cpufreq_target_index,
0152 .get = bmips_cpufreq_get,
0153 .init = bmips_cpufreq_init,
0154 .exit = bmips_cpufreq_exit,
0155 .attr = cpufreq_generic_attr,
0156 .name = BMIPS_CPUFREQ_PREFIX,
0157 };
0158
0159 static int __init bmips_cpufreq_probe(void)
0160 {
0161 struct cpufreq_compat *cc;
0162 struct device_node *np;
0163
0164 for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
0165 np = of_find_compatible_node(NULL, "cpu", cc->compatible);
0166 if (np) {
0167 of_node_put(np);
0168 priv = cc;
0169 break;
0170 }
0171 }
0172
0173
0174 if (!cc->compatible)
0175 return -ENODEV;
0176
0177 return cpufreq_register_driver(&bmips_cpufreq_driver);
0178 }
0179 device_initcall(bmips_cpufreq_probe);
0180
0181 MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
0182 MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
0183 MODULE_LICENSE("GPL");