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(current, cpumask_of_cpu(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        }
 106        vco.v = cm_osc & 255;
 107        vco.r = 22;
 108        freqs.old = icst_hz(&cclk_params, vco) / 1000;
 109
 110        /* icst_hz_to_vco rounds down -- so we need the next
 111         * larger freq in case of CPUFREQ_RELATION_L.
 112         */
 113        if (relation == CPUFREQ_RELATION_L)
 114                target_freq += 999;
 115        if (target_freq > policy->max)
 116                target_freq = policy->max;
 117        vco = icst_hz_to_vco(&cclk_params, target_freq * 1000);
 118        freqs.new = icst_hz(&cclk_params, vco) / 1000;
 119
 120        if (freqs.old == freqs.new) {
 121                set_cpus_allowed(current, cpus_allowed);
 122                return 0;
 123        }
 124
 125        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 126
 127        cm_osc = __raw_readl(cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 128
 129        if (machine_is_integrator()) {
 130                cm_osc &= 0xfffff800;
 131                cm_osc |= vco.s << 8;
 132        } else if (machine_is_cintegrator()) {
 133                cm_osc &= 0xffffff00;
 134        }
 135        cm_osc |= vco.v;
 136
 137        __raw_writel(0xa05f, cm_base + INTEGRATOR_HDR_LOCK_OFFSET);
 138        __raw_writel(cm_osc, cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 139        __raw_writel(0, cm_base + INTEGRATOR_HDR_LOCK_OFFSET);
 140
 141        /*
 142         * Restore the CPUs allowed mask.
 143         */
 144        set_cpus_allowed(current, cpus_allowed);
 145
 146        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 147
 148        return 0;
 149}
 150
 151static unsigned int integrator_get(unsigned int cpu)
 152{
 153        cpumask_t cpus_allowed;
 154        unsigned int current_freq;
 155        u_int cm_osc;
 156        struct icst_vco vco;
 157
 158        cpus_allowed = current->cpus_allowed;
 159
 160        set_cpus_allowed(current, cpumask_of_cpu(cpu));
 161        BUG_ON(cpu != smp_processor_id());
 162
 163        /* detect memory etc. */
 164        cm_osc = __raw_readl(cm_base + INTEGRATOR_HDR_OSC_OFFSET);
 165
 166        if (machine_is_integrator()) {
 167                vco.s = (cm_osc >> 8) & 7;
 168        } else {
 169                vco.s = 1;
 170        }
 171        vco.v = cm_osc & 255;
 172        vco.r = 22;
 173
 174        current_freq = icst_hz(&cclk_params, vco) / 1000; /* current freq */
 175
 176        set_cpus_allowed(current, cpus_allowed);
 177
 178        return current_freq;
 179}
 180
 181static int integrator_cpufreq_init(struct cpufreq_policy *policy)
 182{
 183
 184        /* set default policy and cpuinfo */
 185        policy->max = policy->cpuinfo.max_freq = 160000;
 186        policy->min = policy->cpuinfo.min_freq = 12000;
 187        policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
 188
 189        return 0;
 190}
 191
 192static struct cpufreq_driver integrator_driver = {
 193        .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
 194        .verify         = integrator_verify_policy,
 195        .target         = integrator_set_target,
 196        .get            = integrator_get,
 197        .init           = integrator_cpufreq_init,
 198        .name           = "integrator",
 199};
 200
 201static int __init integrator_cpufreq_probe(struct platform_device *pdev)
 202{
 203        struct resource *res;
 204
 205        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 206        if (!res)
 207                return -ENODEV;
 208
 209        cm_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 210        if (!cm_base)
 211                return -ENODEV;
 212
 213        return cpufreq_register_driver(&integrator_driver);
 214}
 215
 216static void __exit integrator_cpufreq_remove(struct platform_device *pdev)
 217{
 218        cpufreq_unregister_driver(&integrator_driver);
 219}
 220
 221static const struct of_device_id integrator_cpufreq_match[] = {
 222        { .compatible = "arm,core-module-integrator"},
 223        { },
 224};
 225
 226static struct platform_driver integrator_cpufreq_driver = {
 227        .driver = {
 228                .name = "integrator-cpufreq",
 229                .owner = THIS_MODULE,
 230                .of_match_table = integrator_cpufreq_match,
 231        },
 232        .remove = __exit_p(integrator_cpufreq_remove),
 233};
 234
 235module_platform_driver_probe(integrator_cpufreq_driver,
 236                             integrator_cpufreq_probe);
 237
 238MODULE_AUTHOR ("Russell M. King");
 239MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs");
 240MODULE_LICENSE ("GPL");
 241
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.