linux/drivers/cpufreq/integrator-cpufreq.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2001-2002 Deep Blue Solutions Ltd.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * CPU support functions
   9 */
  10#include <linux/module.h>
  11#include <linux/types.h>
  12#include <linux/kernel.h>
  13#include <linux/cpufreq.h>
  14#include <linux/sched.h>
  15#include <linux/smp.h>
  16#include <linux/init.h>
  17#include <linux/io.h>
  18
  19#include <mach/hardware.h>
  20#include <mach/platform.h>
  21#include <asm/mach-types.h>
  22#include <asm/hardware/icst.h>
  23
  24static struct cpufreq_driver integrator_driver;
  25
  26#define CM_ID   __io_address(INTEGRATOR_HDR_ID)
  27#define CM_OSC  __io_address(INTEGRATOR_HDR_OSC)
  28#define CM_STAT __io_address(INTEGRATOR_HDR_STAT)
  29#define CM_LOCK __io_address(INTEGRATOR_HDR_LOCK)
  30
  31static const struct icst_params lclk_params = {
  32        .ref            = 24000000,
  33        .vco_max        = ICST525_VCO_MAX_5V,
  34        .vco_min        = ICST525_VCO_MIN,
  35        .vd_min         = 8,
  36        .vd_max         = 132,
  37        .rd_min         = 24,
  38        .rd_max         = 24,
  39        .s2div          = icst525_s2div,
  40        .idx2s          = icst525_idx2s,
  41};
  42
  43static const struct icst_params cclk_params = {
  44        .ref            = 24000000,
  45        .vco_max        = ICST525_VCO_MAX_5V,
  46        .vco_min        = ICST525_VCO_MIN,
  47        .vd_min         = 12,
  48        .vd_max         = 160,
  49        .rd_min         = 24,
  50        .rd_max         = 24,
  51        .s2div          = icst525_s2div,
  52        .idx2s          = icst525_idx2s,
  53};
  54
  55/*
  56 * Validate the speed policy.
  57 */
  58static int integrator_verify_policy(struct cpufreq_policy *policy)
  59{
  60        struct icst_vco vco;
  61
  62        cpufreq_verify_within_limits(policy, 
  63                                     policy->cpuinfo.min_freq, 
  64                                     policy->cpuinfo.max_freq);
  65
  66        vco = icst_hz_to_vco(&cclk_params, policy->max * 1000);
  67        policy->max = icst_hz(&cclk_params, vco) / 1000;
  68
  69        vco = icst_hz_to_vco(&cclk_params, policy->min * 1000);
  70        policy->min = icst_hz(&cclk_params, vco) / 1000;
  71
  72        cpufreq_verify_within_limits(policy, 
  73                                     policy->cpuinfo.min_freq, 
  74                                     policy->cpuinfo.max_freq);
  75
  76        return 0;
  77}
  78
  79
  80static int integrator_set_target(struct cpufreq_policy *policy,
  81                                 unsigned int target_freq,
  82                                 unsigned int relation)
  83{
  84        cpumask_t cpus_allowed;
  85        int cpu = policy->cpu;
  86        struct icst_vco vco;
  87        struct cpufreq_freqs freqs;
  88        u_int cm_osc;
  89
  90        /*
  91         * Save this threads cpus_allowed mask.
  92         */
  93        cpus_allowed = current->cpus_allowed;
  94
  95        /*
  96         * Bind to the specified CPU.  When this call returns,
  97         * we should be running on the right CPU.
  98         */
  99        set_cpus_allowed(current, cpumask_of_cpu(cpu));
 100        BUG_ON(cpu != smp_processor_id());
 101
 102        /* get current setting */
 103        cm_osc = __raw_readl(CM_OSC);
 104
 105        if (machine_is_integrator()) {
 106                vco.s = (cm_osc >> 8) & 7;
 107        } else if (machine_is_cintegrator()) {
 108                vco.s = 1;
 109        }
 110        vco.v = cm_osc & 255;
 111        vco.r = 22;
 112        freqs.old = icst_hz(&cclk_params, vco) / 1000;
 113
 114        /* icst_hz_to_vco rounds down -- so we need the next
 115         * larger freq in case of CPUFREQ_RELATION_L.
 116         */
 117        if (relation == CPUFREQ_RELATION_L)
 118                target_freq += 999;
 119        if (target_freq > policy->max)
 120                target_freq = policy->max;
 121        vco = icst_hz_to_vco(&cclk_params, target_freq * 1000);
 122        freqs.new = icst_hz(&cclk_params, vco) / 1000;
 123
 124        if (freqs.old == freqs.new) {
 125                set_cpus_allowed(current, cpus_allowed);
 126                return 0;
 127        }
 128
 129        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 130
 131        cm_osc = __raw_readl(CM_OSC);
 132
 133        if (machine_is_integrator()) {
 134                cm_osc &= 0xfffff800;
 135                cm_osc |= vco.s << 8;
 136        } else if (machine_is_cintegrator()) {
 137                cm_osc &= 0xffffff00;
 138        }
 139        cm_osc |= vco.v;
 140
 141        __raw_writel(0xa05f, CM_LOCK);
 142        __raw_writel(cm_osc, CM_OSC);
 143        __raw_writel(0, CM_LOCK);
 144
 145        /*
 146         * Restore the CPUs allowed mask.
 147         */
 148        set_cpus_allowed(current, cpus_allowed);
 149
 150        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 151
 152        return 0;
 153}
 154
 155static unsigned int integrator_get(unsigned int cpu)
 156{
 157        cpumask_t cpus_allowed;
 158        unsigned int current_freq;
 159        u_int cm_osc;
 160        struct icst_vco vco;
 161
 162        cpus_allowed = current->cpus_allowed;
 163
 164        set_cpus_allowed(current, cpumask_of_cpu(cpu));
 165        BUG_ON(cpu != smp_processor_id());
 166
 167        /* detect memory etc. */
 168        cm_osc = __raw_readl(CM_OSC);
 169
 170        if (machine_is_integrator()) {
 171                vco.s = (cm_osc >> 8) & 7;
 172        } else {
 173                vco.s = 1;
 174        }
 175        vco.v = cm_osc & 255;
 176        vco.r = 22;
 177
 178        current_freq = icst_hz(&cclk_params, vco) / 1000; /* current freq */
 179
 180        set_cpus_allowed(current, cpus_allowed);
 181
 182        return current_freq;
 183}
 184
 185static int integrator_cpufreq_init(struct cpufreq_policy *policy)
 186{
 187
 188        /* set default policy and cpuinfo */
 189        policy->cpuinfo.max_freq = 160000;
 190        policy->cpuinfo.min_freq = 12000;
 191        policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
 192        policy->cur = policy->min = policy->max = integrator_get(policy->cpu);
 193
 194        return 0;
 195}
 196
 197static struct cpufreq_driver integrator_driver = {
 198        .verify         = integrator_verify_policy,
 199        .target         = integrator_set_target,
 200        .get            = integrator_get,
 201        .init           = integrator_cpufreq_init,
 202        .name           = "integrator",
 203};
 204
 205static int __init integrator_cpu_init(void)
 206{
 207        return cpufreq_register_driver(&integrator_driver);
 208}
 209
 210static void __exit integrator_cpu_exit(void)
 211{
 212        cpufreq_unregister_driver(&integrator_driver);
 213}
 214
 215MODULE_AUTHOR ("Russell M. King");
 216MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs");
 217MODULE_LICENSE ("GPL");
 218
 219module_init(integrator_cpu_init);
 220module_exit(integrator_cpu_exit);
 221
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.