Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * CPU frequency scaling for Broadcom BMIPS SoCs
0003  *
0004  * Copyright (c) 2017 Broadcom
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation version 2.
0009  *
0010  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
0011  * kind, whether express or implied; without even the implied warranty
0012  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
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 /* for mips_hpt_frequency */
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) /* 25 us */
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     /* We hit the guard element of the array. No compatible CPU found. */
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");