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#include <linux/platform_device.h>
  19#include <linux/of.h>
  20#include <linux/of_address.h>
  21
  22#include <asm/mach-types.h>
  23#include <asm/hardware/icst.h>
  24
  25static void __iomem *cm_base;
  26/* The cpufreq driver only use the OSC register */
  27#define INTEGRATOR_HDR_OSC_OFFSET       0x08
  28#define INTEGRATOR_HDR_LOCK_OFFSET      0x14
  29
  30static struct cpufreq_driver integrator_driver;
  31
  32static const struct icst_params lclk_params = {
  33        .ref            = 24000000,
  34        .vco_max        = ICST525_VCO_MAX_5V,
  35        .vco_min        = ICST525_VCO_MIN,
  36        .vd_min         = 8,
  37        .vd_max         = 132,
  38        .rd_min         = 24,
  39        .rd_max         = 24,
  40        .s2div          = icst525_s2div,
  41        .idx2s          = icst525_idx2s,
  42};
  43
  44static const struct icst_params cclk_params = {
  45        .ref            = 24000000,
  46        .vco_max        = ICST525_VCO_MAX_5V,
  47        .vco_min        = ICST525_VCO_MIN,
  48        .vd_min         = 12,
  49        .vd_max         = 160,
  50        .rd_min         = 24,
  51        .rd_max         = 24,
  52        .s2div          = icst525_s2div,
  53        .idx2s          = icst525_idx2s,
  54};
  55
  56/*
  57 * Validate the speed policy.
  58 */
  59static int integrator_verify_policy(struct cpufreq_policy *policy)
  60{
  61        struct icst_vco vco;
  62
  63        cpufreq_verify_within_cpu_limits(policy);
  64
  65        vco = icst_hz_to_vco(&cclk_params, policy->max * 1000);
  66        policy->max = icst_hz(&cclk_params, vco) / 1000;
  67
  68        vco = icst_hz_to_vco(&cclk_params, policy->min * 1000);
  69        policy->min = icst_hz(&cclk_params, vco) / 1000;
  70
  71        cpufreq_verify_within_cpu_limits(policy);
  72        return 0;
  73}
  74
  75
  76static int integrator_set_target(struct cpufreq_policy *policy,
  77                                 unsigned int target_freq,
  78                                 unsigned int relation)
  79{
  80        cpumask_t cpus_allowed;
  81        int cpu = policy->cpu;
  82        struct icst_vco vco;
  83        struct cpufreq_freqs freqs;
  84        u_int cm_osc;
  85
  86        /*
  87         * Save this threads cpus_allowed mask.
  88         */
  89        cpus_allowed = current->cpus_allowed;
  90
  91        /*
  92         * Bind to the specified CPU.  When this call returns,
  93         * we should be running on the right CPU.
  94         */
  95        set_cpus_allowed_ptr(current, cpumask_of(cpu));
  96        BUG_ON(cpu != smp_processor_id());
  97
  98        /* get current setting */
  99        cm_osc = __raw_readl(cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 100
 101        if (machine_is_integrator())
 102                vco.s = (cm_osc >> 8) & 7;
 103        else if (machine_is_cintegrator())
 104                vco.s = 1;
 105        vco.v = cm_osc & 255;
 106        vco.r = 22;
 107        freqs.old = icst_hz(&cclk_params, vco) / 1000;
 108
 109        /* icst_hz_to_vco rounds down -- so we need the next
 110         * larger freq in case of CPUFREQ_RELATION_L.
 111         */
 112        if (relation == CPUFREQ_RELATION_L)
 113                target_freq += 999;
 114        if (target_freq > policy->max)
 115                target_freq = policy->max;
 116        vco = icst_hz_to_vco(&cclk_params, target_freq * 1000);
 117        freqs.new = icst_hz(&cclk_params, vco) / 1000;
 118
 119        if (freqs.old == freqs.new) {
 120                set_cpus_allowed_ptr(current, &cpus_allowed);
 121                return 0;
 122        }
 123
 124        cpufreq_freq_transition_begin(policy, &freqs);
 125
 126        cm_osc = __raw_readl(cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 127
 128        if (machine_is_integrator()) {
 129                cm_osc &= 0xfffff800;
 130                cm_osc |= vco.s << 8;
 131        } else if (machine_is_cintegrator()) {
 132                cm_osc &= 0xffffff00;
 133        }
 134        cm_osc |= vco.v;
 135
 136        __raw_writel(0xa05f, cm_base + INTEGRATOR_HDR_LOCK_OFFSET);
 137        __raw_writel(cm_osc, cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 138        __raw_writel(0, cm_base + INTEGRATOR_HDR_LOCK_OFFSET);
 139
 140        /*
 141         * Restore the CPUs allowed mask.
 142         */
 143        set_cpus_allowed_ptr(current, &cpus_allowed);
 144
 145        cpufreq_freq_transition_end(policy, &freqs, 0);
 146
 147        return 0;
 148}
 149
 150static unsigned int integrator_get(unsigned int cpu)
 151{
 152        cpumask_t cpus_allowed;
 153        unsigned int current_freq;
 154        u_int cm_osc;
 155        struct icst_vco vco;
 156
 157        cpus_allowed = current->cpus_allowed;
 158
 159        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 160        BUG_ON(cpu != smp_processor_id());
 161
 162        /* detect memory etc. */
 163        cm_osc = __raw_readl(cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 164
 165        if (machine_is_integrator())
 166                vco.s = (cm_osc >> 8) & 7;
 167        else
 168                vco.s = 1;
 169        vco.v = cm_osc & 255;
 170        vco.r = 22;
 171
 172        current_freq = icst_hz(&cclk_params, vco) / 1000; /* current freq */
 173
 174        set_cpus_allowed_ptr(current, &cpus_allowed);
 175
 176        return current_freq;
 177}
 178
 179static int integrator_cpufreq_init(struct cpufreq_policy *policy)
 180{
 181
 182        /* set default policy and cpuinfo */
 183        policy->max = policy->cpuinfo.max_freq = 160000;
 184        policy->min = policy->cpuinfo.min_freq = 12000;
 185        policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
 186
 187        return 0;
 188}
 189
 190static struct cpufreq_driver integrator_driver = {
 191        .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
 192        .verify         = integrator_verify_policy,
 193        .target         = integrator_set_target,
 194        .get            = integrator_get,
 195        .init           = integrator_cpufreq_init,
 196        .name           = "integrator",
 197};
 198
 199static int __init integrator_cpufreq_probe(struct platform_device *pdev)
 200{
 201        struct resource *res;
 202
 203        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 204        if (!res)
 205                return -ENODEV;
 206
 207        cm_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 208        if (!cm_base)
 209                return -ENODEV;
 210
 211        return cpufreq_register_driver(&integrator_driver);
 212}
 213
 214static int __exit integrator_cpufreq_remove(struct platform_device *pdev)
 215{
 216        return cpufreq_unregister_driver(&integrator_driver);
 217}
 218
 219static const struct of_device_id integrator_cpufreq_match[] = {
 220        { .compatible = "arm,core-module-integrator"},
 221        { },
 222};
 223
 224MODULE_DEVICE_TABLE(of, integrator_cpufreq_match);
 225
 226static struct platform_driver integrator_cpufreq_driver = {
 227        .driver = {
 228                .name = "integrator-cpufreq",
 229                .of_match_table = integrator_cpufreq_match,
 230        },
 231        .remove = __exit_p(integrator_cpufreq_remove),
 232};
 233
 234module_platform_driver_probe(integrator_cpufreq_driver,
 235                             integrator_cpufreq_probe);
 236
 237MODULE_AUTHOR("Russell M. King");
 238MODULE_DESCRIPTION("cpufreq driver for ARM Integrator CPUs");
 239MODULE_LICENSE("GPL");
 240
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.