linux/drivers/cpufreq/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 =
 208                        get_astate_freq(pas_freqs[i].driver_data) * 100000;
 209                pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
 210        }
 211
 212        policy->cpuinfo.transition_latency = get_gizmo_latency();
 213
 214        cur_astate = get_cur_astate(policy->cpu);
 215        pr_debug("current astate is at %d\n",cur_astate);
 216
 217        policy->cur = pas_freqs[cur_astate].frequency;
 218        cpumask_copy(policy->cpus, cpu_online_mask);
 219
 220        ppc_proc_freq = policy->cur * 1000ul;
 221
 222        cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
 223
 224        /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
 225         * are set correctly
 226         */
 227        return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
 228
 229out_unmap_sdcpwr:
 230        iounmap(sdcpwr_mapbase);
 231
 232out_unmap_sdcasr:
 233        iounmap(sdcasr_mapbase);
 234out:
 235        return err;
 236}
 237
 238static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 239{
 240        /*
 241         * We don't support CPU hotplug. Don't unmap after the system
 242         * has already made it to a running state.
 243         */
 244        if (system_state != SYSTEM_BOOTING)
 245                return 0;
 246
 247        if (sdcasr_mapbase)
 248                iounmap(sdcasr_mapbase);
 249        if (sdcpwr_mapbase)
 250                iounmap(sdcpwr_mapbase);
 251
 252        cpufreq_frequency_table_put_attr(policy->cpu);
 253        return 0;
 254}
 255
 256static int pas_cpufreq_verify(struct cpufreq_policy *policy)
 257{
 258        return cpufreq_frequency_table_verify(policy, pas_freqs);
 259}
 260
 261static int pas_cpufreq_target(struct cpufreq_policy *policy,
 262                              unsigned int target_freq,
 263                              unsigned int relation)
 264{
 265        struct cpufreq_freqs freqs;
 266        int pas_astate_new;
 267        int i;
 268
 269        cpufreq_frequency_table_target(policy,
 270                                       pas_freqs,
 271                                       target_freq,
 272                                       relation,
 273                                       &pas_astate_new);
 274
 275        freqs.old = policy->cur;
 276        freqs.new = pas_freqs[pas_astate_new].frequency;
 277
 278        mutex_lock(&pas_switch_mutex);
 279        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 280
 281        pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
 282                 policy->cpu,
 283                 pas_freqs[pas_astate_new].frequency,
 284                 pas_freqs[pas_astate_new].driver_data);
 285
 286        current_astate = pas_astate_new;
 287
 288        for_each_online_cpu(i)
 289                set_astate(i, pas_astate_new);
 290
 291        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 292        mutex_unlock(&pas_switch_mutex);
 293
 294        ppc_proc_freq = freqs.new * 1000ul;
 295        return 0;
 296}
 297
 298static struct cpufreq_driver pas_cpufreq_driver = {
 299        .name           = "pas-cpufreq",
 300        .flags          = CPUFREQ_CONST_LOOPS,
 301        .init           = pas_cpufreq_cpu_init,
 302        .exit           = pas_cpufreq_cpu_exit,
 303        .verify         = pas_cpufreq_verify,
 304        .target         = pas_cpufreq_target,
 305        .attr           = pas_cpu_freqs_attr,
 306};
 307
 308/*
 309 * module init and destoy
 310 */
 311
 312static int __init pas_cpufreq_init(void)
 313{
 314        if (!of_machine_is_compatible("PA6T-1682M") &&
 315            !of_machine_is_compatible("pasemi,pwrficient"))
 316                return -ENODEV;
 317
 318        return cpufreq_register_driver(&pas_cpufreq_driver);
 319}
 320
 321static void __exit pas_cpufreq_exit(void)
 322{
 323        cpufreq_unregister_driver(&pas_cpufreq_driver);
 324}
 325
 326module_init(pas_cpufreq_init);
 327module_exit(pas_cpufreq_exit);
 328
 329MODULE_LICENSE("GPL");
 330MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
 331
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.