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 void read_safari_cfg(void *arg)
  39{
  40        unsigned long ret, *val = arg;
  41
  42        __asm__ __volatile__("ldxa      [%%g0] %1, %0"
  43                             : "=&r" (ret)
  44                             : "i" (ASI_SAFARI_CONFIG));
  45        *val = ret;
  46}
  47
  48static void update_safari_cfg(void *arg)
  49{
  50        unsigned long reg, *new_bits = arg;
  51
  52        read_safari_cfg(&reg);
  53        reg &= ~SAFARI_CFG_DIV_MASK;
  54        reg |= *new_bits;
  55
  56        __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
  57                             "membar    #Sync"
  58                             : /* no outputs */
  59                             : "r" (reg), "i" (ASI_SAFARI_CONFIG)
  60                             : "memory");
  61}
  62
  63static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
  64{
  65        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
  66        unsigned long ret;
  67
  68        switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
  69        case SAFARI_CFG_DIV_1:
  70                ret = clock_tick / 1;
  71                break;
  72        case SAFARI_CFG_DIV_2:
  73                ret = clock_tick / 2;
  74                break;
  75        case SAFARI_CFG_DIV_32:
  76                ret = clock_tick / 32;
  77                break;
  78        default:
  79                BUG();
  80        }
  81
  82        return ret;
  83}
  84
  85static unsigned int us3_freq_get(unsigned int cpu)
  86{
  87        unsigned long reg;
  88
  89        if (smp_call_function_single(cpu, read_safari_cfg, &reg, 1))
  90                return 0;
  91        return get_current_freq(cpu, reg);
  92}
  93
  94static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
  95{
  96        unsigned int cpu = policy->cpu;
  97        unsigned long new_bits, new_freq;
  98
  99        new_freq = sparc64_get_clock_tick(cpu) / 1000;
 100        switch (index) {
 101        case 0:
 102                new_bits = SAFARI_CFG_DIV_1;
 103                new_freq /= 1;
 104                break;
 105        case 1:
 106                new_bits = SAFARI_CFG_DIV_2;
 107                new_freq /= 2;
 108                break;
 109        case 2:
 110                new_bits = SAFARI_CFG_DIV_32;
 111                new_freq /= 32;
 112                break;
 113
 114        default:
 115                BUG();
 116        }
 117
 118        return smp_call_function_single(cpu, update_safari_cfg, &new_bits, 1);
 119}
 120
 121static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
 122{
 123        unsigned int cpu = policy->cpu;
 124        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 125        struct cpufreq_frequency_table *table =
 126                &us3_freq_table[cpu].table[0];
 127
 128        table[0].driver_data = 0;
 129        table[0].frequency = clock_tick / 1;
 130        table[1].driver_data = 1;
 131        table[1].frequency = clock_tick / 2;
 132        table[2].driver_data = 2;
 133        table[2].frequency = clock_tick / 32;
 134        table[3].driver_data = 0;
 135        table[3].frequency = CPUFREQ_TABLE_END;
 136
 137        policy->cpuinfo.transition_latency = 0;
 138        policy->cur = clock_tick;
 139        policy->freq_table = table;
 140
 141        return 0;
 142}
 143
 144static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
 145{
 146        if (cpufreq_us3_driver)
 147                us3_freq_target(policy, 0);
 148
 149        return 0;
 150}
 151
 152static int __init us3_freq_init(void)
 153{
 154        unsigned long manuf, impl, ver;
 155        int ret;
 156
 157        if (tlb_type != cheetah && tlb_type != cheetah_plus)
 158                return -ENODEV;
 159
 160        __asm__("rdpr %%ver, %0" : "=r" (ver));
 161        manuf = ((ver >> 48) & 0xffff);
 162        impl  = ((ver >> 32) & 0xffff);
 163
 164        if (manuf == CHEETAH_MANUF &&
 165            (impl == CHEETAH_IMPL ||
 166             impl == CHEETAH_PLUS_IMPL ||
 167             impl == JAGUAR_IMPL ||
 168             impl == PANTHER_IMPL)) {
 169                struct cpufreq_driver *driver;
 170
 171                ret = -ENOMEM;
 172                driver = kzalloc(sizeof(*driver), GFP_KERNEL);
 173                if (!driver)
 174                        goto err_out;
 175
 176                us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
 177                        GFP_KERNEL);
 178                if (!us3_freq_table)
 179                        goto err_out;
 180
 181                driver->init = us3_freq_cpu_init;
 182                driver->verify = cpufreq_generic_frequency_table_verify;
 183                driver->target_index = us3_freq_target;
 184                driver->get = us3_freq_get;
 185                driver->exit = us3_freq_cpu_exit;
 186                strcpy(driver->name, "UltraSPARC-III");
 187
 188                cpufreq_us3_driver = driver;
 189                ret = cpufreq_register_driver(driver);
 190                if (ret)
 191                        goto err_out;
 192
 193                return 0;
 194
 195err_out:
 196                if (driver) {
 197                        kfree(driver);
 198                        cpufreq_us3_driver = NULL;
 199                }
 200                kfree(us3_freq_table);
 201                us3_freq_table = NULL;
 202                return ret;
 203        }
 204
 205        return -ENODEV;
 206}
 207
 208static void __exit us3_freq_exit(void)
 209{
 210        if (cpufreq_us3_driver) {
 211                cpufreq_unregister_driver(cpufreq_us3_driver);
 212                kfree(cpufreq_us3_driver);
 213                cpufreq_us3_driver = NULL;
 214                kfree(us3_freq_table);
 215                us3_freq_table = NULL;
 216        }
 217}
 218
 219MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 220MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
 221MODULE_LICENSE("GPL");
 222
 223module_init(us3_freq_init);
 224module_exit(us3_freq_exit);
 225