linux/arch/arm/plat-omap/cpu-omap.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/plat-omap/cpu-omap.c
   3 *
   4 *  CPU frequency scaling for OMAP
   5 *
   6 *  Copyright (C) 2005 Nokia Corporation
   7 *  Written by Tony Lindgren <tony@atomide.com>
   8 *
   9 *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 */
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/sched.h>
  18#include <linux/cpufreq.h>
  19#include <linux/delay.h>
  20#include <linux/init.h>
  21#include <linux/err.h>
  22#include <linux/clk.h>
  23#include <linux/io.h>
  24
  25#include <mach/hardware.h>
  26#include <mach/clock.h>
  27#include <asm/system.h>
  28
  29#define VERY_HI_RATE    900000000
  30
  31static struct cpufreq_frequency_table *freq_table;
  32
  33#ifdef CONFIG_ARCH_OMAP1
  34#define MPU_CLK         "mpu"
  35#else
  36#define MPU_CLK         "virt_prcm_set"
  37#endif
  38
  39static struct clk *mpu_clk;
  40
  41/* TODO: Add support for SDRAM timing changes */
  42
  43int omap_verify_speed(struct cpufreq_policy *policy)
  44{
  45        if (freq_table)
  46                return cpufreq_frequency_table_verify(policy, freq_table);
  47
  48        if (policy->cpu)
  49                return -EINVAL;
  50
  51        cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
  52                                     policy->cpuinfo.max_freq);
  53
  54        policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000;
  55        policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000;
  56        cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
  57                                     policy->cpuinfo.max_freq);
  58        return 0;
  59}
  60
  61unsigned int omap_getspeed(unsigned int cpu)
  62{
  63        unsigned long rate;
  64
  65        if (cpu)
  66                return 0;
  67
  68        rate = clk_get_rate(mpu_clk) / 1000;
  69        return rate;
  70}
  71
  72static int omap_target(struct cpufreq_policy *policy,
  73                       unsigned int target_freq,
  74                       unsigned int relation)
  75{
  76        struct cpufreq_freqs freqs;
  77        int ret = 0;
  78
  79        /* Ensure desired rate is within allowed range.  Some govenors
  80         * (ondemand) will just pass target_freq=0 to get the minimum. */
  81        if (target_freq < policy->min)
  82                target_freq = policy->min;
  83        if (target_freq > policy->max)
  84                target_freq = policy->max;
  85
  86        freqs.old = omap_getspeed(0);
  87        freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
  88        freqs.cpu = 0;
  89
  90        if (freqs.old == freqs.new)
  91                return ret;
  92
  93        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  94#ifdef CONFIG_CPU_FREQ_DEBUG
  95        printk(KERN_DEBUG "cpufreq-omap: transition: %u --> %u\n",
  96               freqs.old, freqs.new);
  97#endif
  98        ret = clk_set_rate(mpu_clk, freqs.new * 1000);
  99        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 100
 101        return ret;
 102}
 103
 104static int __init omap_cpu_init(struct cpufreq_policy *policy)
 105{
 106        int result = 0;
 107
 108        mpu_clk = clk_get(NULL, MPU_CLK);
 109        if (IS_ERR(mpu_clk))
 110                return PTR_ERR(mpu_clk);
 111
 112        if (policy->cpu != 0)
 113                return -EINVAL;
 114
 115        policy->cur = policy->min = policy->max = omap_getspeed(0);
 116
 117        clk_init_cpufreq_table(&freq_table);
 118        if (freq_table) {
 119                result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
 120                if (!result)
 121                        cpufreq_frequency_table_get_attr(freq_table,
 122                                                        policy->cpu);
 123        } else {
 124                policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
 125                policy->cpuinfo.max_freq = clk_round_rate(mpu_clk,
 126                                                        VERY_HI_RATE) / 1000;
 127        }
 128
 129        /* FIXME: what's the actual transition time? */
 130        policy->cpuinfo.transition_latency = 300 * 1000;
 131
 132        return 0;
 133}
 134
 135static int omap_cpu_exit(struct cpufreq_policy *policy)
 136{
 137        clk_put(mpu_clk);
 138        return 0;
 139}
 140
 141static struct freq_attr *omap_cpufreq_attr[] = {
 142        &cpufreq_freq_attr_scaling_available_freqs,
 143        NULL,
 144};
 145
 146static struct cpufreq_driver omap_driver = {
 147        .flags          = CPUFREQ_STICKY,
 148        .verify         = omap_verify_speed,
 149        .target         = omap_target,
 150        .get            = omap_getspeed,
 151        .init           = omap_cpu_init,
 152        .exit           = omap_cpu_exit,
 153        .name           = "omap",
 154        .attr           = omap_cpufreq_attr,
 155};
 156
 157static int __init omap_cpufreq_init(void)
 158{
 159        return cpufreq_register_driver(&omap_driver);
 160}
 161
 162arch_initcall(omap_cpufreq_init);
 163
 164/*
 165 * if ever we want to remove this, upon cleanup call:
 166 *
 167 * cpufreq_unregister_driver()
 168 * cpufreq_frequency_table_put_attr()
 169 */
 170
 171