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