linux/drivers/cpufreq/qoriq-cpufreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2013 Freescale Semiconductor, Inc.
   4 *
   5 * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
   6 */
   7
   8#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
   9
  10#include <linux/clk.h>
  11#include <linux/clk-provider.h>
  12#include <linux/cpufreq.h>
  13#include <linux/errno.h>
  14#include <linux/init.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/mutex.h>
  18#include <linux/of.h>
  19#include <linux/slab.h>
  20#include <linux/smp.h>
  21
  22/**
  23 * struct cpu_data
  24 * @pclk: the parent clock of cpu
  25 * @table: frequency table
  26 */
  27struct cpu_data {
  28        struct clk **pclk;
  29        struct cpufreq_frequency_table *table;
  30};
  31
  32/*
  33 * Don't use cpufreq on this SoC -- used when the SoC would have otherwise
  34 * matched a more generic compatible.
  35 */
  36#define SOC_BLACKLIST           1
  37
  38/**
  39 * struct soc_data - SoC specific data
  40 * @flags: SOC_xxx
  41 */
  42struct soc_data {
  43        u32 flags;
  44};
  45
  46static u32 get_bus_freq(void)
  47{
  48        struct device_node *soc;
  49        u32 sysfreq;
  50        struct clk *pltclk;
  51        int ret;
  52
  53        /* get platform freq by searching bus-frequency property */
  54        soc = of_find_node_by_type(NULL, "soc");
  55        if (soc) {
  56                ret = of_property_read_u32(soc, "bus-frequency", &sysfreq);
  57                of_node_put(soc);
  58                if (!ret)
  59                        return sysfreq;
  60        }
  61
  62        /* get platform freq by its clock name */
  63        pltclk = clk_get(NULL, "cg-pll0-div1");
  64        if (IS_ERR(pltclk)) {
  65                pr_err("%s: can't get bus frequency %ld\n",
  66                       __func__, PTR_ERR(pltclk));
  67                return PTR_ERR(pltclk);
  68        }
  69
  70        return clk_get_rate(pltclk);
  71}
  72
  73static struct clk *cpu_to_clk(int cpu)
  74{
  75        struct device_node *np;
  76        struct clk *clk;
  77
  78        if (!cpu_present(cpu))
  79                return NULL;
  80
  81        np = of_get_cpu_node(cpu, NULL);
  82        if (!np)
  83                return NULL;
  84
  85        clk = of_clk_get(np, 0);
  86        of_node_put(np);
  87        return clk;
  88}
  89
  90/* traverse cpu nodes to get cpu mask of sharing clock wire */
  91static void set_affected_cpus(struct cpufreq_policy *policy)
  92{
  93        struct cpumask *dstp = policy->cpus;
  94        struct clk *clk;
  95        int i;
  96
  97        for_each_present_cpu(i) {
  98                clk = cpu_to_clk(i);
  99                if (IS_ERR(clk)) {
 100                        pr_err("%s: no clock for cpu %d\n", __func__, i);
 101                        continue;
 102                }
 103
 104                if (clk_is_match(policy->clk, clk))
 105                        cpumask_set_cpu(i, dstp);
 106        }
 107}
 108
 109/* reduce the duplicated frequencies in frequency table */
 110static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
 111                int count)
 112{
 113        int i, j;
 114
 115        for (i = 1; i < count; i++) {
 116                for (j = 0; j < i; j++) {
 117                        if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID ||
 118                                        freq_table[j].frequency !=
 119                                        freq_table[i].frequency)
 120                                continue;
 121
 122                        freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 123                        break;
 124                }
 125        }
 126}
 127
 128/* sort the frequencies in frequency table in descenting order */
 129static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
 130                int count)
 131{
 132        int i, j, ind;
 133        unsigned int freq, max_freq;
 134        struct cpufreq_frequency_table table;
 135
 136        for (i = 0; i < count - 1; i++) {
 137                max_freq = freq_table[i].frequency;
 138                ind = i;
 139                for (j = i + 1; j < count; j++) {
 140                        freq = freq_table[j].frequency;
 141                        if (freq == CPUFREQ_ENTRY_INVALID ||
 142                                        freq <= max_freq)
 143                                continue;
 144                        ind = j;
 145                        max_freq = freq;
 146                }
 147
 148                if (ind != i) {
 149                        /* exchange the frequencies */
 150                        table.driver_data = freq_table[i].driver_data;
 151                        table.frequency = freq_table[i].frequency;
 152                        freq_table[i].driver_data = freq_table[ind].driver_data;
 153                        freq_table[i].frequency = freq_table[ind].frequency;
 154                        freq_table[ind].driver_data = table.driver_data;
 155                        freq_table[ind].frequency = table.frequency;
 156                }
 157        }
 158}
 159
 160static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
 161{
 162        struct device_node *np;
 163        int i, count;
 164        u32 freq;
 165        struct clk *clk;
 166        const struct clk_hw *hwclk;
 167        struct cpufreq_frequency_table *table;
 168        struct cpu_data *data;
 169        unsigned int cpu = policy->cpu;
 170        u64 u64temp;
 171
 172        np = of_get_cpu_node(cpu, NULL);
 173        if (!np)
 174                return -ENODEV;
 175
 176        data = kzalloc(sizeof(*data), GFP_KERNEL);
 177        if (!data)
 178                goto err_np;
 179
 180        policy->clk = of_clk_get(np, 0);
 181        if (IS_ERR(policy->clk)) {
 182                pr_err("%s: no clock information\n", __func__);
 183                goto err_nomem2;
 184        }
 185
 186        hwclk = __clk_get_hw(policy->clk);
 187        count = clk_hw_get_num_parents(hwclk);
 188
 189        data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
 190        if (!data->pclk)
 191                goto err_nomem2;
 192
 193        table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
 194        if (!table)
 195                goto err_pclk;
 196
 197        for (i = 0; i < count; i++) {
 198                clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
 199                data->pclk[i] = clk;
 200                freq = clk_get_rate(clk);
 201                table[i].frequency = freq / 1000;
 202                table[i].driver_data = i;
 203        }
 204        freq_table_redup(table, count);
 205        freq_table_sort(table, count);
 206        table[i].frequency = CPUFREQ_TABLE_END;
 207        policy->freq_table = table;
 208        data->table = table;
 209
 210        /* update ->cpus if we have cluster, no harm if not */
 211        set_affected_cpus(policy);
 212        policy->driver_data = data;
 213
 214        /* Minimum transition latency is 12 platform clocks */
 215        u64temp = 12ULL * NSEC_PER_SEC;
 216        do_div(u64temp, get_bus_freq());
 217        policy->cpuinfo.transition_latency = u64temp + 1;
 218
 219        of_node_put(np);
 220
 221        return 0;
 222
 223err_pclk:
 224        kfree(data->pclk);
 225err_nomem2:
 226        kfree(data);
 227err_np:
 228        of_node_put(np);
 229
 230        return -ENODEV;
 231}
 232
 233static int qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 234{
 235        struct cpu_data *data = policy->driver_data;
 236
 237        kfree(data->pclk);
 238        kfree(data->table);
 239        kfree(data);
 240        policy->driver_data = NULL;
 241
 242        return 0;
 243}
 244
 245static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
 246                unsigned int index)
 247{
 248        struct clk *parent;
 249        struct cpu_data *data = policy->driver_data;
 250
 251        parent = data->pclk[data->table[index].driver_data];
 252        return clk_set_parent(policy->clk, parent);
 253}
 254
 255static struct cpufreq_driver qoriq_cpufreq_driver = {
 256        .name           = "qoriq_cpufreq",
 257        .flags          = CPUFREQ_CONST_LOOPS |
 258                          CPUFREQ_IS_COOLING_DEV,
 259        .init           = qoriq_cpufreq_cpu_init,
 260        .exit           = qoriq_cpufreq_cpu_exit,
 261        .verify         = cpufreq_generic_frequency_table_verify,
 262        .target_index   = qoriq_cpufreq_target,
 263        .get            = cpufreq_generic_get,
 264        .attr           = cpufreq_generic_attr,
 265};
 266
 267static const struct soc_data blacklist = {
 268        .flags = SOC_BLACKLIST,
 269};
 270
 271static const struct of_device_id node_matches[] __initconst = {
 272        /* e6500 cannot use cpufreq due to erratum A-008083 */
 273        { .compatible = "fsl,b4420-clockgen", &blacklist },
 274        { .compatible = "fsl,b4860-clockgen", &blacklist },
 275        { .compatible = "fsl,t2080-clockgen", &blacklist },
 276        { .compatible = "fsl,t4240-clockgen", &blacklist },
 277
 278        { .compatible = "fsl,ls1012a-clockgen", },
 279        { .compatible = "fsl,ls1021a-clockgen", },
 280        { .compatible = "fsl,ls1028a-clockgen", },
 281        { .compatible = "fsl,ls1043a-clockgen", },
 282        { .compatible = "fsl,ls1046a-clockgen", },
 283        { .compatible = "fsl,ls1088a-clockgen", },
 284        { .compatible = "fsl,ls2080a-clockgen", },
 285        { .compatible = "fsl,lx2160a-clockgen", },
 286        { .compatible = "fsl,p4080-clockgen", },
 287        { .compatible = "fsl,qoriq-clockgen-1.0", },
 288        { .compatible = "fsl,qoriq-clockgen-2.0", },
 289        {}
 290};
 291
 292static int __init qoriq_cpufreq_init(void)
 293{
 294        int ret;
 295        struct device_node  *np;
 296        const struct of_device_id *match;
 297        const struct soc_data *data;
 298
 299        np = of_find_matching_node(NULL, node_matches);
 300        if (!np)
 301                return -ENODEV;
 302
 303        match = of_match_node(node_matches, np);
 304        data = match->data;
 305
 306        of_node_put(np);
 307
 308        if (data && data->flags & SOC_BLACKLIST)
 309                return -ENODEV;
 310
 311        ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
 312        if (!ret)
 313                pr_info("Freescale QorIQ CPU frequency scaling driver\n");
 314
 315        return ret;
 316}
 317module_init(qoriq_cpufreq_init);
 318
 319static void __exit qoriq_cpufreq_exit(void)
 320{
 321        cpufreq_unregister_driver(&qoriq_cpufreq_driver);
 322}
 323module_exit(qoriq_cpufreq_exit);
 324
 325MODULE_LICENSE("GPL");
 326MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
 327MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
 328