linux/drivers/cpufreq/sparc-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        cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
  86        set_cpus_allowed_ptr(current, cpumask_of(cpu));
  87
  88        reg = read_safari_cfg();
  89        ret = get_current_freq(cpu, reg);
  90
  91        set_cpus_allowed_ptr(current, &cpus_allowed);
  92
  93        return ret;
  94}
  95
  96static void us3_set_cpu_divider_index(struct cpufreq_policy *policy,
  97                unsigned int index)
  98{
  99        unsigned int cpu = policy->cpu;
 100        unsigned long new_bits, new_freq, reg;
 101        cpumask_t cpus_allowed;
 102        struct cpufreq_freqs freqs;
 103
 104        cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
 105        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 106
 107        new_freq = sparc64_get_clock_tick(cpu) / 1000;
 108        switch (index) {
 109        case 0:
 110                new_bits = SAFARI_CFG_DIV_1;
 111                new_freq /= 1;
 112                break;
 113        case 1:
 114                new_bits = SAFARI_CFG_DIV_2;
 115                new_freq /= 2;
 116                break;
 117        case 2:
 118                new_bits = SAFARI_CFG_DIV_32;
 119                new_freq /= 32;
 120                break;
 121
 122        default:
 123                BUG();
 124        }
 125
 126        reg = read_safari_cfg();
 127
 128        freqs.old = get_current_freq(cpu, reg);
 129        freqs.new = new_freq;
 130        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 131
 132        reg &= ~SAFARI_CFG_DIV_MASK;
 133        reg |= new_bits;
 134        write_safari_cfg(reg);
 135
 136        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 137
 138        set_cpus_allowed_ptr(current, &cpus_allowed);
 139}
 140
 141static int us3_freq_target(struct cpufreq_policy *policy,
 142                          unsigned int target_freq,
 143                          unsigned int relation)
 144{
 145        unsigned int new_index = 0;
 146
 147        if (cpufreq_frequency_table_target(policy,
 148                                           &us3_freq_table[policy->cpu].table[0],
 149                                           target_freq,
 150                                           relation,
 151                                           &new_index))
 152                return -EINVAL;
 153
 154        us3_set_cpu_divider_index(policy, new_index);
 155
 156        return 0;
 157}
 158
 159static int us3_freq_verify(struct cpufreq_policy *policy)
 160{
 161        return cpufreq_frequency_table_verify(policy,
 162                                              &us3_freq_table[policy->cpu].table[0]);
 163}
 164
 165static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
 166{
 167        unsigned int cpu = policy->cpu;
 168        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 169        struct cpufreq_frequency_table *table =
 170                &us3_freq_table[cpu].table[0];
 171
 172        table[0].index = 0;
 173        table[0].frequency = clock_tick / 1;
 174        table[1].index = 1;
 175        table[1].frequency = clock_tick / 2;
 176        table[2].index = 2;
 177        table[2].frequency = clock_tick / 32;
 178        table[3].index = 0;
 179        table[3].frequency = CPUFREQ_TABLE_END;
 180
 181        policy->cpuinfo.transition_latency = 0;
 182        policy->cur = clock_tick;
 183
 184        return cpufreq_frequency_table_cpuinfo(policy, table);
 185}
 186
 187static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
 188{
 189        if (cpufreq_us3_driver)
 190                us3_set_cpu_divider_index(policy, 0);
 191
 192        return 0;
 193}
 194
 195static int __init us3_freq_init(void)
 196{
 197        unsigned long manuf, impl, ver;
 198        int ret;
 199
 200        if (tlb_type != cheetah && tlb_type != cheetah_plus)
 201                return -ENODEV;
 202
 203        __asm__("rdpr %%ver, %0" : "=r" (ver));
 204        manuf = ((ver >> 48) & 0xffff);
 205        impl  = ((ver >> 32) & 0xffff);
 206
 207        if (manuf == CHEETAH_MANUF &&
 208            (impl == CHEETAH_IMPL ||
 209             impl == CHEETAH_PLUS_IMPL ||
 210             impl == JAGUAR_IMPL ||
 211             impl == PANTHER_IMPL)) {
 212                struct cpufreq_driver *driver;
 213
 214                ret = -ENOMEM;
 215                driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
 216                if (!driver)
 217                        goto err_out;
 218
 219                us3_freq_table = kzalloc(
 220                        (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
 221                        GFP_KERNEL);
 222                if (!us3_freq_table)
 223                        goto err_out;
 224
 225                driver->init = us3_freq_cpu_init;
 226                driver->verify = us3_freq_verify;
 227                driver->target = us3_freq_target;
 228                driver->get = us3_freq_get;
 229                driver->exit = us3_freq_cpu_exit;
 230                driver->owner = THIS_MODULE,
 231                strcpy(driver->name, "UltraSPARC-III");
 232
 233                cpufreq_us3_driver = driver;
 234                ret = cpufreq_register_driver(driver);
 235                if (ret)
 236                        goto err_out;
 237
 238                return 0;
 239
 240err_out:
 241                if (driver) {
 242                        kfree(driver);
 243                        cpufreq_us3_driver = NULL;
 244                }
 245                kfree(us3_freq_table);
 246                us3_freq_table = NULL;
 247                return ret;
 248        }
 249
 250        return -ENODEV;
 251}
 252
 253static void __exit us3_freq_exit(void)
 254{
 255        if (cpufreq_us3_driver) {
 256                cpufreq_unregister_driver(cpufreq_us3_driver);
 257                kfree(cpufreq_us3_driver);
 258                cpufreq_us3_driver = NULL;
 259                kfree(us3_freq_table);
 260                us3_freq_table = NULL;
 261        }
 262}
 263
 264MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 265MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
 266MODULE_LICENSE("GPL");
 267
 268module_init(us3_freq_init);
 269module_exit(us3_freq_exit);
 270