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#include <linux/of_address.h>
  32
  33#include <asm/hw_irq.h>
  34#include <asm/io.h>
  35#include <asm/prom.h>
  36#include <asm/time.h>
  37#include <asm/smp.h>
  38
  39#define SDCASR_REG              0x0100
  40#define SDCASR_REG_STRIDE       0x1000
  41#define SDCPWR_CFGA0_REG        0x0100
  42#define SDCPWR_PWST0_REG        0x0000
  43#define SDCPWR_GIZTIME_REG      0x0440
  44
  45/* SDCPWR_GIZTIME_REG fields */
  46#define SDCPWR_GIZTIME_GR       0x80000000
  47#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
  48
  49/* Offset of ASR registers from SDC base */
  50#define SDCASR_OFFSET           0x120000
  51
  52static void __iomem *sdcpwr_mapbase;
  53static void __iomem *sdcasr_mapbase;
  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,  0},
  64        {0, 1,  0},
  65        {0, 2,  0},
  66        {0, 3,  0},
  67        {0, 4,  0},
  68        {0, 0,  CPUFREQ_TABLE_END},
  69};
  70
  71/*
  72 * hardware specific functions
  73 */
  74
  75static int get_astate_freq(int astate)
  76{
  77        u32 ret;
  78        ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
  79
  80        return ret & 0x3f;
  81}
  82
  83static int get_cur_astate(int cpu)
  84{
  85        u32 ret;
  86
  87        ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
  88        ret = (ret >> (cpu * 4)) & 0x7;
  89
  90        return ret;
  91}
  92
  93static int get_gizmo_latency(void)
  94{
  95        u32 giztime, ret;
  96
  97        giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
  98
  99        /* just provide the upper bound */
 100        if (giztime & SDCPWR_GIZTIME_GR)
 101                ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
 102        else
 103                ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
 104
 105        return ret;
 106}
 107
 108static void set_astate(int cpu, unsigned int astate)
 109{
 110        unsigned long flags;
 111
 112        /* Return if called before init has run */
 113        if (unlikely(!sdcasr_mapbase))
 114                return;
 115
 116        local_irq_save(flags);
 117
 118        out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
 119
 120        local_irq_restore(flags);
 121}
 122
 123int check_astate(void)
 124{
 125        return get_cur_astate(hard_smp_processor_id());
 126}
 127
 128void restore_astate(int cpu)
 129{
 130        set_astate(cpu, current_astate);
 131}
 132
 133/*
 134 * cpufreq functions
 135 */
 136
 137static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
 138{
 139        struct cpufreq_frequency_table *pos;
 140        const u32 *max_freqp;
 141        u32 max_freq;
 142        int cur_astate;
 143        struct resource res;
 144        struct device_node *cpu, *dn;
 145        int err = -ENODEV;
 146
 147        cpu = of_get_cpu_node(policy->cpu, NULL);
 148
 149        if (!cpu)
 150                goto out;
 151
 152        dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
 153        if (!dn)
 154                dn = of_find_compatible_node(NULL, NULL,
 155                                             "pasemi,pwrficient-sdc");
 156        if (!dn)
 157                goto out;
 158        err = of_address_to_resource(dn, 0, &res);
 159        of_node_put(dn);
 160        if (err)
 161                goto out;
 162        sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
 163        if (!sdcasr_mapbase) {
 164                err = -EINVAL;
 165                goto out;
 166        }
 167
 168        dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo");
 169        if (!dn)
 170                dn = of_find_compatible_node(NULL, NULL,
 171                                             "pasemi,pwrficient-gizmo");
 172        if (!dn) {
 173                err = -ENODEV;
 174                goto out_unmap_sdcasr;
 175        }
 176        err = of_address_to_resource(dn, 0, &res);
 177        of_node_put(dn);
 178        if (err)
 179                goto out_unmap_sdcasr;
 180        sdcpwr_mapbase = ioremap(res.start, 0x1000);
 181        if (!sdcpwr_mapbase) {
 182                err = -EINVAL;
 183                goto out_unmap_sdcasr;
 184        }
 185
 186        pr_debug("init cpufreq on CPU %d\n", policy->cpu);
 187
 188        max_freqp = of_get_property(cpu, "clock-frequency", NULL);
 189        if (!max_freqp) {
 190                err = -EINVAL;
 191                goto out_unmap_sdcpwr;
 192        }
 193
 194        /* we need the freq in kHz */
 195        max_freq = *max_freqp / 1000;
 196
 197        pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 198        pr_debug("initializing frequency table\n");
 199
 200        /* initialize frequency table */
 201        cpufreq_for_each_entry(pos, pas_freqs) {
 202                pos->frequency = get_astate_freq(pos->driver_data) * 100000;
 203                pr_debug("%d: %d\n", (int)(pos - pas_freqs), pos->frequency);
 204        }
 205
 206        cur_astate = get_cur_astate(policy->cpu);
 207        pr_debug("current astate is at %d\n",cur_astate);
 208
 209        policy->cur = pas_freqs[cur_astate].frequency;
 210        ppc_proc_freq = policy->cur * 1000ul;
 211
 212        return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
 213
 214out_unmap_sdcpwr:
 215        iounmap(sdcpwr_mapbase);
 216
 217out_unmap_sdcasr:
 218        iounmap(sdcasr_mapbase);
 219out:
 220        return err;
 221}
 222
 223static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 224{
 225        /*
 226         * We don't support CPU hotplug. Don't unmap after the system
 227         * has already made it to a running state.
 228         */
 229        if (system_state >= SYSTEM_RUNNING)
 230                return 0;
 231
 232        if (sdcasr_mapbase)
 233                iounmap(sdcasr_mapbase);
 234        if (sdcpwr_mapbase)
 235                iounmap(sdcpwr_mapbase);
 236
 237        return 0;
 238}
 239
 240static int pas_cpufreq_target(struct cpufreq_policy *policy,
 241                              unsigned int pas_astate_new)
 242{
 243        int i;
 244
 245        pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
 246                 policy->cpu,
 247                 pas_freqs[pas_astate_new].frequency,
 248                 pas_freqs[pas_astate_new].driver_data);
 249
 250        current_astate = pas_astate_new;
 251
 252        for_each_online_cpu(i)
 253                set_astate(i, pas_astate_new);
 254
 255        ppc_proc_freq = pas_freqs[pas_astate_new].frequency * 1000ul;
 256        return 0;
 257}
 258
 259static struct cpufreq_driver pas_cpufreq_driver = {
 260        .name           = "pas-cpufreq",
 261        .flags          = CPUFREQ_CONST_LOOPS,
 262        .init           = pas_cpufreq_cpu_init,
 263        .exit           = pas_cpufreq_cpu_exit,
 264        .verify         = cpufreq_generic_frequency_table_verify,
 265        .target_index   = pas_cpufreq_target,
 266        .attr           = cpufreq_generic_attr,
 267};
 268
 269/*
 270 * module init and destoy
 271 */
 272
 273static int __init pas_cpufreq_init(void)
 274{
 275        if (!of_machine_is_compatible("PA6T-1682M") &&
 276            !of_machine_is_compatible("pasemi,pwrficient"))
 277                return -ENODEV;
 278
 279        return cpufreq_register_driver(&pas_cpufreq_driver);
 280}
 281
 282static void __exit pas_cpufreq_exit(void)
 283{
 284        cpufreq_unregister_driver(&pas_cpufreq_driver);
 285}
 286
 287module_init(pas_cpufreq_init);
 288module_exit(pas_cpufreq_exit);
 289
 290MODULE_LICENSE("GPL");
 291MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
 292