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 int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
  97{
  98        unsigned int cpu = policy->cpu;
  99        unsigned long new_bits, new_freq, reg;
 100        cpumask_t cpus_allowed;
 101
 102        cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
 103        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 104
 105        new_freq = sparc64_get_clock_tick(cpu) / 1000;
 106        switch (index) {
 107        case 0:
 108                new_bits = SAFARI_CFG_DIV_1;
 109                new_freq /= 1;
 110                break;
 111        case 1:
 112                new_bits = SAFARI_CFG_DIV_2;
 113                new_freq /= 2;
 114                break;
 115        case 2:
 116                new_bits = SAFARI_CFG_DIV_32;
 117                new_freq /= 32;
 118                break;
 119
 120        default:
 121                BUG();
 122        }
 123
 124        reg = read_safari_cfg();
 125
 126        reg &= ~SAFARI_CFG_DIV_MASK;
 127        reg |= new_bits;
 128        write_safari_cfg(reg);
 129
 130        set_cpus_allowed_ptr(current, &cpus_allowed);
 131
 132        return 0;
 133}
 134
 135static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
 136{
 137        unsigned int cpu = policy->cpu;
 138        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 139        struct cpufreq_frequency_table *table =
 140                &us3_freq_table[cpu].table[0];
 141
 142        table[0].driver_data = 0;
 143        table[0].frequency = clock_tick / 1;
 144        table[1].driver_data = 1;
 145        table[1].frequency = clock_tick / 2;
 146        table[2].driver_data = 2;
 147        table[2].frequency = clock_tick / 32;
 148        table[3].driver_data = 0;
 149        table[3].frequency = CPUFREQ_TABLE_END;
 150
 151        policy->cpuinfo.transition_latency = 0;
 152        policy->cur = clock_tick;
 153
 154        return cpufreq_table_validate_and_show(policy, table);
 155}
 156
 157static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
 158{
 159        if (cpufreq_us3_driver)
 160                us3_freq_target(policy, 0);
 161
 162        return 0;
 163}
 164
 165static int __init us3_freq_init(void)
 166{
 167        unsigned long manuf, impl, ver;
 168        int ret;
 169
 170        if (tlb_type != cheetah && tlb_type != cheetah_plus)
 171                return -ENODEV;
 172
 173        __asm__("rdpr %%ver, %0" : "=r" (ver));
 174        manuf = ((ver >> 48) & 0xffff);
 175        impl  = ((ver >> 32) & 0xffff);
 176
 177        if (manuf == CHEETAH_MANUF &&
 178            (impl == CHEETAH_IMPL ||
 179             impl == CHEETAH_PLUS_IMPL ||
 180             impl == JAGUAR_IMPL ||
 181             impl == PANTHER_IMPL)) {
 182                struct cpufreq_driver *driver;
 183
 184                ret = -ENOMEM;
 185                driver = kzalloc(sizeof(*driver), GFP_KERNEL);
 186                if (!driver)
 187                        goto err_out;
 188
 189                us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
 190                        GFP_KERNEL);
 191                if (!us3_freq_table)
 192                        goto err_out;
 193
 194                driver->init = us3_freq_cpu_init;
 195                driver->verify = cpufreq_generic_frequency_table_verify;
 196                driver->target_index = us3_freq_target;
 197                driver->get = us3_freq_get;
 198                driver->exit = us3_freq_cpu_exit;
 199                strcpy(driver->name, "UltraSPARC-III");
 200
 201                cpufreq_us3_driver = driver;
 202                ret = cpufreq_register_driver(driver);
 203                if (ret)
 204                        goto err_out;
 205
 206                return 0;
 207
 208err_out:
 209                if (driver) {
 210                        kfree(driver);
 211                        cpufreq_us3_driver = NULL;
 212                }
 213                kfree(us3_freq_table);
 214                us3_freq_table = NULL;
 215                return ret;
 216        }
 217
 218        return -ENODEV;
 219}
 220
 221static void __exit us3_freq_exit(void)
 222{
 223        if (cpufreq_us3_driver) {
 224                cpufreq_unregister_driver(cpufreq_us3_driver);
 225                kfree(cpufreq_us3_driver);
 226                cpufreq_us3_driver = NULL;
 227                kfree(us3_freq_table);
 228                us3_freq_table = NULL;
 229        }
 230}
 231
 232MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 233MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
 234MODULE_LICENSE("GPL");
 235
 236module_init(us3_freq_init);
 237module_exit(us3_freq_exit);
 238