linux/drivers/cpufreq/dbx500-cpufreq.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) STMicroelectronics 2009
   3 * Copyright (C) ST-Ericsson SA 2010-2012
   4 *
   5 * License Terms: GNU General Public License v2
   6 * Author: Sundar Iyer <sundar.iyer@stericsson.com>
   7 * Author: Martin Persson <martin.persson@stericsson.com>
   8 * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/cpufreq.h>
  14#include <linux/delay.h>
  15#include <linux/slab.h>
  16#include <linux/platform_device.h>
  17#include <linux/clk.h>
  18
  19static struct cpufreq_frequency_table *freq_table;
  20static struct clk *armss_clk;
  21
  22static struct freq_attr *dbx500_cpufreq_attr[] = {
  23        &cpufreq_freq_attr_scaling_available_freqs,
  24        NULL,
  25};
  26
  27static int dbx500_cpufreq_verify_speed(struct cpufreq_policy *policy)
  28{
  29        return cpufreq_frequency_table_verify(policy, freq_table);
  30}
  31
  32static int dbx500_cpufreq_target(struct cpufreq_policy *policy,
  33                                unsigned int target_freq,
  34                                unsigned int relation)
  35{
  36        struct cpufreq_freqs freqs;
  37        unsigned int idx;
  38        int ret;
  39
  40        /* Lookup the next frequency */
  41        if (cpufreq_frequency_table_target(policy, freq_table, target_freq,
  42                                        relation, &idx))
  43                return -EINVAL;
  44
  45        freqs.old = policy->cur;
  46        freqs.new = freq_table[idx].frequency;
  47
  48        if (freqs.old == freqs.new)
  49                return 0;
  50
  51        /* pre-change notification */
  52        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
  53
  54        /* update armss clk frequency */
  55        ret = clk_set_rate(armss_clk, freqs.new * 1000);
  56
  57        if (ret) {
  58                pr_err("dbx500-cpufreq: Failed to set armss_clk to %d Hz: error %d\n",
  59                       freqs.new * 1000, ret);
  60                return ret;
  61        }
  62
  63        /* post change notification */
  64        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
  65
  66        return 0;
  67}
  68
  69static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu)
  70{
  71        int i = 0;
  72        unsigned long freq = clk_get_rate(armss_clk) / 1000;
  73
  74        /* The value is rounded to closest frequency in the defined table. */
  75        while (freq_table[i + 1].frequency != CPUFREQ_TABLE_END) {
  76                if (freq < freq_table[i].frequency +
  77                   (freq_table[i + 1].frequency - freq_table[i].frequency) / 2)
  78                        return freq_table[i].frequency;
  79                i++;
  80        }
  81
  82        return freq_table[i].frequency;
  83}
  84
  85static int dbx500_cpufreq_init(struct cpufreq_policy *policy)
  86{
  87        int res;
  88
  89        /* get policy fields based on the table */
  90        res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
  91        if (!res)
  92                cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
  93        else {
  94                pr_err("dbx500-cpufreq: Failed to read policy table\n");
  95                return res;
  96        }
  97
  98        policy->min = policy->cpuinfo.min_freq;
  99        policy->max = policy->cpuinfo.max_freq;
 100        policy->cur = dbx500_cpufreq_getspeed(policy->cpu);
 101        policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 102
 103        /*
 104         * FIXME : Need to take time measurement across the target()
 105         *         function with no/some/all drivers in the notification
 106         *         list.
 107         */
 108        policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
 109
 110        /* policy sharing between dual CPUs */
 111        cpumask_setall(policy->cpus);
 112
 113        return 0;
 114}
 115
 116static struct cpufreq_driver dbx500_cpufreq_driver = {
 117        .flags  = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS,
 118        .verify = dbx500_cpufreq_verify_speed,
 119        .target = dbx500_cpufreq_target,
 120        .get    = dbx500_cpufreq_getspeed,
 121        .init   = dbx500_cpufreq_init,
 122        .name   = "DBX500",
 123        .attr   = dbx500_cpufreq_attr,
 124};
 125
 126static int dbx500_cpufreq_probe(struct platform_device *pdev)
 127{
 128        int i = 0;
 129
 130        freq_table = dev_get_platdata(&pdev->dev);
 131        if (!freq_table) {
 132                pr_err("dbx500-cpufreq: Failed to fetch cpufreq table\n");
 133                return -ENODEV;
 134        }
 135
 136        armss_clk = clk_get(&pdev->dev, "armss");
 137        if (IS_ERR(armss_clk)) {
 138                pr_err("dbx500-cpufreq: Failed to get armss clk\n");
 139                return PTR_ERR(armss_clk);
 140        }
 141
 142        pr_info("dbx500-cpufreq: Available frequencies:\n");
 143        while (freq_table[i].frequency != CPUFREQ_TABLE_END) {
 144                pr_info("  %d Mhz\n", freq_table[i].frequency/1000);
 145                i++;
 146        }
 147
 148        return cpufreq_register_driver(&dbx500_cpufreq_driver);
 149}
 150
 151static struct platform_driver dbx500_cpufreq_plat_driver = {
 152        .driver = {
 153                .name = "cpufreq-ux500",
 154                .owner = THIS_MODULE,
 155        },
 156        .probe = dbx500_cpufreq_probe,
 157};
 158
 159static int __init dbx500_cpufreq_register(void)
 160{
 161        return platform_driver_register(&dbx500_cpufreq_plat_driver);
 162}
 163device_initcall(dbx500_cpufreq_register);
 164
 165MODULE_LICENSE("GPL v2");
 166MODULE_DESCRIPTION("cpufreq driver for DBX500");
 167