linux/arch/sparc/kernel/us3_cpufreq.c
<<
>>
Prefs
   1/* us3_cpufreq.c: UltraSPARC-III cpu frequency support
   2 *
   3 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
   4 *
   5 * Many thanks to Dominik Brodowski for fixing up the cpufreq
   6 * infrastructure in order to make this driver easier to implement.
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/sched.h>
  12#include <linux/smp.h>
  13#include <linux/cpufreq.h>
  14#include <linux/threads.h>
  15#include <linux/slab.h>
  16#include <linux/init.h>
  17
  18#include <asm/head.h>
  19#include <asm/timer.h>
  20
  21static struct cpufreq_driver *cpufreq_us3_driver;
  22
  23struct us3_freq_percpu_info {
  24        struct cpufreq_frequency_table table[4];
  25};
  26
  27/* Indexed by cpu number. */
  28static struct us3_freq_percpu_info *us3_freq_table;
  29
  30/* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
  31 * in the Safari config register.
  32 */
  33#define SAFARI_CFG_DIV_1        0x0000000000000000UL
  34#define SAFARI_CFG_DIV_2        0x0000000040000000UL
  35#define SAFARI_CFG_DIV_32       0x0000000080000000UL
  36#define SAFARI_CFG_DIV_MASK     0x00000000C0000000UL
  37
  38static unsigned long read_safari_cfg(void)
  39{
  40        unsigned long ret;
  41
  42        __asm__ __volatile__("ldxa      [%%g0] %1, %0"
  43                             : "=&r" (ret)
  44                             : "i" (ASI_SAFARI_CONFIG));
  45        return ret;
  46}
  47
  48static void write_safari_cfg(unsigned long val)
  49{
  50        __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
  51                             "membar    #Sync"
  52                             : /* no outputs */
  53                             : "r" (val), "i" (ASI_SAFARI_CONFIG)
  54                             : "memory");
  55}
  56
  57static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
  58{
  59        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
  60        unsigned long ret;
  61
  62        switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
  63        case SAFARI_CFG_DIV_1:
  64                ret = clock_tick / 1;
  65                break;
  66        case SAFARI_CFG_DIV_2:
  67                ret = clock_tick / 2;
  68                break;
  69        case SAFARI_CFG_DIV_32:
  70                ret = clock_tick / 32;
  71                break;
  72        default:
  73                BUG();
  74        };
  75
  76        return ret;
  77}
  78
  79static unsigned int us3_freq_get(unsigned int cpu)
  80{
  81        cpumask_t cpus_allowed;
  82        unsigned long reg;
  83        unsigned int ret;
  84
  85        if (!cpu_online(cpu))
  86                return 0;
  87
  88        cpus_allowed = current->cpus_allowed;
  89        set_cpus_allowed(current, cpumask_of_cpu(cpu));
  90
  91        reg = read_safari_cfg();
  92        ret = get_current_freq(cpu, reg);
  93
  94        set_cpus_allowed(current, cpus_allowed);
  95
  96        return ret;
  97}
  98
  99static void us3_set_cpu_divider_index(unsigned int cpu, unsigned int index)
 100{
 101        unsigned long new_bits, new_freq, reg;
 102        cpumask_t cpus_allowed;
 103        struct cpufreq_freqs freqs;
 104
 105        if (!cpu_online(cpu))
 106                return;
 107
 108        cpus_allowed = current->cpus_allowed;
 109        set_cpus_allowed(current, cpumask_of_cpu(cpu));
 110
 111        new_freq = sparc64_get_clock_tick(cpu) / 1000;
 112        switch (index) {
 113        case 0:
 114                new_bits = SAFARI_CFG_DIV_1;
 115                new_freq /= 1;
 116                break;
 117        case 1:
 118                new_bits = SAFARI_CFG_DIV_2;
 119                new_freq /= 2;
 120                break;
 121        case 2:
 122                new_bits = SAFARI_CFG_DIV_32;
 123                new_freq /= 32;
 124                break;
 125
 126        default:
 127                BUG();
 128        };
 129
 130        reg = read_safari_cfg();
 131
 132        freqs.old = get_current_freq(cpu, reg);
 133        freqs.new = new_freq;
 134        freqs.cpu = cpu;
 135        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 136
 137        reg &= ~SAFARI_CFG_DIV_MASK;
 138        reg |= new_bits;
 139        write_safari_cfg(reg);
 140
 141        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 142
 143        set_cpus_allowed(current, cpus_allowed);
 144}
 145
 146static int us3_freq_target(struct cpufreq_policy *policy,
 147                          unsigned int target_freq,
 148                          unsigned int relation)
 149{
 150        unsigned int new_index = 0;
 151
 152        if (cpufreq_frequency_table_target(policy,
 153                                           &us3_freq_table[policy->cpu].table[0],
 154                                           target_freq,
 155                                           relation,
 156                                           &new_index))
 157                return -EINVAL;
 158
 159        us3_set_cpu_divider_index(policy->cpu, new_index);
 160
 161        return 0;
 162}
 163
 164static int us3_freq_verify(struct cpufreq_policy *policy)
 165{
 166        return cpufreq_frequency_table_verify(policy,
 167                                              &us3_freq_table[policy->cpu].table[0]);
 168}
 169
 170static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
 171{
 172        unsigned int cpu = policy->cpu;
 173        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 174        struct cpufreq_frequency_table *table =
 175                &us3_freq_table[cpu].table[0];
 176
 177        table[0].index = 0;
 178        table[0].frequency = clock_tick / 1;
 179        table[1].index = 1;
 180        table[1].frequency = clock_tick / 2;
 181        table[2].index = 2;
 182        table[2].frequency = clock_tick / 32;
 183        table[3].index = 0;
 184        table[3].frequency = CPUFREQ_TABLE_END;
 185
 186        policy->cpuinfo.transition_latency = 0;
 187        policy->cur = clock_tick;
 188
 189        return cpufreq_frequency_table_cpuinfo(policy, table);
 190}
 191
 192static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
 193{
 194        if (cpufreq_us3_driver)
 195                us3_set_cpu_divider_index(policy->cpu, 0);
 196
 197        return 0;
 198}
 199
 200static int __init us3_freq_init(void)
 201{
 202        unsigned long manuf, impl, ver;
 203        int ret;
 204
 205        if (tlb_type != cheetah && tlb_type != cheetah_plus)
 206                return -ENODEV;
 207
 208        __asm__("rdpr %%ver, %0" : "=r" (ver));
 209        manuf = ((ver >> 48) & 0xffff);
 210        impl  = ((ver >> 32) & 0xffff);
 211
 212        if (manuf == CHEETAH_MANUF &&
 213            (impl == CHEETAH_IMPL ||
 214             impl == CHEETAH_PLUS_IMPL ||
 215             impl == JAGUAR_IMPL ||
 216             impl == PANTHER_IMPL)) {
 217                struct cpufreq_driver *driver;
 218
 219                ret = -ENOMEM;
 220                driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
 221                if (!driver)
 222                        goto err_out;
 223
 224                us3_freq_table = kzalloc(
 225                        (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
 226                        GFP_KERNEL);
 227                if (!us3_freq_table)
 228                        goto err_out;
 229
 230                driver->init = us3_freq_cpu_init;
 231                driver->verify = us3_freq_verify;
 232                driver->target = us3_freq_target;
 233                driver->get = us3_freq_get;
 234                driver->exit = us3_freq_cpu_exit;
 235                driver->owner = THIS_MODULE,
 236                strcpy(driver->name, "UltraSPARC-III");
 237
 238                cpufreq_us3_driver = driver;
 239                ret = cpufreq_register_driver(driver);
 240                if (ret)
 241                        goto err_out;
 242
 243                return 0;
 244
 245err_out:
 246                if (driver) {
 247                        kfree(driver);
 248                        cpufreq_us3_driver = NULL;
 249                }
 250                kfree(us3_freq_table);
 251                us3_freq_table = NULL;
 252                return ret;
 253        }
 254
 255        return -ENODEV;
 256}
 257
 258static void __exit us3_freq_exit(void)
 259{
 260        if (cpufreq_us3_driver) {
 261                cpufreq_unregister_driver(cpufreq_us3_driver);
 262                kfree(cpufreq_us3_driver);
 263                cpufreq_us3_driver = NULL;
 264                kfree(us3_freq_table);
 265                us3_freq_table = NULL;
 266        }
 267}
 268
 269MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 270MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
 271MODULE_LICENSE("GPL");
 272
 273module_init(us3_freq_init);
 274module_exit(us3_freq_exit);
 275