linux/arch/powerpc/platforms/pasemi/cpufreq.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 PA Semi, Inc
   3 *
   4 * Authors: Egor Martovetsky <egor@pasemi.com>
   5 *          Olof Johansson <olof@lixom.net>
   6 *
   7 * Maintained by: Olof Johansson <olof@lixom.net>
   8 *
   9 * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
  10 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License as published by
  14 * the Free Software Foundation; either version 2, or (at your option)
  15 * any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25 *
  26 */
  27
  28#include <linux/cpufreq.h>
  29#include <linux/timer.h>
  30
  31#include <asm/hw_irq.h>
  32#include <asm/io.h>
  33#include <asm/prom.h>
  34#include <asm/time.h>
  35#include <asm/smp.h>
  36
  37#define SDCASR_REG              0x0100
  38#define SDCASR_REG_STRIDE       0x1000
  39#define SDCPWR_CFGA0_REG        0x0100
  40#define SDCPWR_PWST0_REG        0x0000
  41#define SDCPWR_GIZTIME_REG      0x0440
  42
  43/* SDCPWR_GIZTIME_REG fields */
  44#define SDCPWR_GIZTIME_GR       0x80000000
  45#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
  46
  47/* Offset of ASR registers from SDC base */
  48#define SDCASR_OFFSET           0x120000
  49
  50static void __iomem *sdcpwr_mapbase;
  51static void __iomem *sdcasr_mapbase;
  52
  53static DEFINE_MUTEX(pas_switch_mutex);
  54
  55/* Current astate, is used when waking up from power savings on
  56 * one core, in case the other core has switched states during
  57 * the idle time.
  58 */
  59static int current_astate;
  60
  61/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
  62static struct cpufreq_frequency_table pas_freqs[] = {
  63        {0,     0},
  64        {1,     0},
  65        {2,     0},
  66        {3,     0},
  67        {4,     0},
  68        {0,     CPUFREQ_TABLE_END},
  69};
  70
  71static struct freq_attr *pas_cpu_freqs_attr[] = {
  72        &cpufreq_freq_attr_scaling_available_freqs,
  73        NULL,
  74};
  75
  76/*
  77 * hardware specific functions
  78 */
  79
  80static int get_astate_freq(int astate)
  81{
  82        u32 ret;
  83        ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
  84
  85        return ret & 0x3f;
  86}
  87
  88static int get_cur_astate(int cpu)
  89{
  90        u32 ret;
  91
  92        ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
  93        ret = (ret >> (cpu * 4)) & 0x7;
  94
  95        return ret;
  96}
  97
  98static int get_gizmo_latency(void)
  99{
 100        u32 giztime, ret;
 101
 102        giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
 103
 104        /* just provide the upper bound */
 105        if (giztime & SDCPWR_GIZTIME_GR)
 106                ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
 107        else
 108                ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
 109
 110        return ret;
 111}
 112
 113static void set_astate(int cpu, unsigned int astate)
 114{
 115        u64 flags;
 116
 117        /* Return if called before init has run */
 118        if (unlikely(!sdcasr_mapbase))
 119                return;
 120
 121        local_irq_save(flags);
 122
 123        out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
 124
 125        local_irq_restore(flags);
 126}
 127
 128int check_astate(void)
 129{
 130        return get_cur_astate(hard_smp_processor_id());
 131}
 132
 133void restore_astate(int cpu)
 134{
 135        set_astate(cpu, current_astate);
 136}
 137
 138/*
 139 * cpufreq functions
 140 */
 141
 142static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
 143{
 144        const u32 *max_freqp;
 145        u32 max_freq;
 146        int i, cur_astate;
 147        struct resource res;
 148        struct device_node *cpu, *dn;
 149        int err = -ENODEV;
 150
 151        cpu = of_get_cpu_node(policy->cpu, NULL);
 152
 153        if (!cpu)
 154                goto out;
 155
 156        dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
 157        if (!dn)
 158                dn = of_find_compatible_node(NULL, NULL,
 159                                             "pasemi,pwrficient-sdc");
 160        if (!dn)
 161                goto out;
 162        err = of_address_to_resource(dn, 0, &res);
 163        of_node_put(dn);
 164        if (err)
 165                goto out;
 166        sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
 167        if (!sdcasr_mapbase) {
 168                err = -EINVAL;
 169                goto out;
 170        }
 171
 172        dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo");
 173        if (!dn)
 174                dn = of_find_compatible_node(NULL, NULL,
 175                                             "pasemi,pwrficient-gizmo");
 176        if (!dn) {
 177                err = -ENODEV;
 178                goto out_unmap_sdcasr;
 179        }
 180        err = of_address_to_resource(dn, 0, &res);
 181        of_node_put(dn);
 182        if (err)
 183                goto out_unmap_sdcasr;
 184        sdcpwr_mapbase = ioremap(res.start, 0x1000);
 185        if (!sdcpwr_mapbase) {
 186                err = -EINVAL;
 187                goto out_unmap_sdcasr;
 188        }
 189
 190        pr_debug("init cpufreq on CPU %d\n", policy->cpu);
 191
 192        max_freqp = of_get_property(cpu, "clock-frequency", NULL);
 193        if (!max_freqp) {
 194                err = -EINVAL;
 195                goto out_unmap_sdcpwr;
 196        }
 197
 198        /* we need the freq in kHz */
 199        max_freq = *max_freqp / 1000;
 200
 201        pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 202        pr_debug("initializing frequency table\n");
 203
 204        /* initialize frequency table */
 205        for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
 206                pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
 207                pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
 208        }
 209
 210        policy->cpuinfo.transition_latency = get_gizmo_latency();
 211
 212        cur_astate = get_cur_astate(policy->cpu);
 213        pr_debug("current astate is at %d\n",cur_astate);
 214
 215        policy->cur = pas_freqs[cur_astate].frequency;
 216        policy->cpus = cpu_online_map;
 217
 218        ppc_proc_freq = policy->cur * 1000ul;
 219
 220        cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
 221
 222        /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
 223         * are set correctly
 224         */
 225        return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
 226
 227out_unmap_sdcpwr:
 228        iounmap(sdcpwr_mapbase);
 229
 230out_unmap_sdcasr:
 231        iounmap(sdcasr_mapbase);
 232out:
 233        return err;
 234}
 235
 236static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 237{
 238        if (sdcasr_mapbase)
 239                iounmap(sdcasr_mapbase);
 240        if (sdcpwr_mapbase)
 241                iounmap(sdcpwr_mapbase);
 242
 243        cpufreq_frequency_table_put_attr(policy->cpu);
 244        return 0;
 245}
 246
 247static int pas_cpufreq_verify(struct cpufreq_policy *policy)
 248{
 249        return cpufreq_frequency_table_verify(policy, pas_freqs);
 250}
 251
 252static int pas_cpufreq_target(struct cpufreq_policy *policy,
 253                              unsigned int target_freq,
 254                              unsigned int relation)
 255{
 256        struct cpufreq_freqs freqs;
 257        int pas_astate_new;
 258        int i;
 259
 260        cpufreq_frequency_table_target(policy,
 261                                       pas_freqs,
 262                                       target_freq,
 263                                       relation,
 264                                       &pas_astate_new);
 265
 266        freqs.old = policy->cur;
 267        freqs.new = pas_freqs[pas_astate_new].frequency;
 268        freqs.cpu = policy->cpu;
 269
 270        mutex_lock(&pas_switch_mutex);
 271        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 272
 273        pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
 274                 policy->cpu,
 275                 pas_freqs[pas_astate_new].frequency,
 276                 pas_freqs[pas_astate_new].index);
 277
 278        current_astate = pas_astate_new;
 279
 280        for_each_online_cpu(i)
 281                set_astate(i, pas_astate_new);
 282
 283        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 284        mutex_unlock(&pas_switch_mutex);
 285
 286        ppc_proc_freq = freqs.new * 1000ul;
 287        return 0;
 288}
 289
 290static struct cpufreq_driver pas_cpufreq_driver = {
 291        .name           = "pas-cpufreq",
 292        .owner          = THIS_MODULE,
 293        .flags          = CPUFREQ_CONST_LOOPS,
 294        .init           = pas_cpufreq_cpu_init,
 295        .exit           = pas_cpufreq_cpu_exit,
 296        .verify         = pas_cpufreq_verify,
 297        .target         = pas_cpufreq_target,
 298        .attr           = pas_cpu_freqs_attr,
 299};
 300
 301/*
 302 * module init and destoy
 303 */
 304
 305static int __init pas_cpufreq_init(void)
 306{
 307        if (!machine_is_compatible("PA6T-1682M") &&
 308            !machine_is_compatible("pasemi,pwrficient"))
 309                return -ENODEV;
 310
 311        return cpufreq_register_driver(&pas_cpufreq_driver);
 312}
 313
 314static void __exit pas_cpufreq_exit(void)
 315{
 316        cpufreq_unregister_driver(&pas_cpufreq_driver);
 317}
 318
 319module_init(pas_cpufreq_init);
 320module_exit(pas_cpufreq_exit);
 321
 322MODULE_LICENSE("GPL");
 323MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
 324
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.