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