linux/drivers/cpufreq/sparc-us2e-cpufreq.c
<<
>>
Prefs
   1/* us2e_cpufreq.c: UltraSPARC-IIe 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/delay.h>
  17#include <linux/init.h>
  18
  19#include <asm/asi.h>
  20#include <asm/timer.h>
  21
  22static struct cpufreq_driver *cpufreq_us2e_driver;
  23
  24struct us2e_freq_percpu_info {
  25        struct cpufreq_frequency_table table[6];
  26};
  27
  28/* Indexed by cpu number. */
  29static struct us2e_freq_percpu_info *us2e_freq_table;
  30
  31#define HBIRD_MEM_CNTL0_ADDR    0x1fe0000f010UL
  32#define HBIRD_ESTAR_MODE_ADDR   0x1fe0000f080UL
  33
  34/* UltraSPARC-IIe has five dividers: 1, 2, 4, 6, and 8.  These are controlled
  35 * in the ESTAR mode control register.
  36 */
  37#define ESTAR_MODE_DIV_1        0x0000000000000000UL
  38#define ESTAR_MODE_DIV_2        0x0000000000000001UL
  39#define ESTAR_MODE_DIV_4        0x0000000000000003UL
  40#define ESTAR_MODE_DIV_6        0x0000000000000002UL
  41#define ESTAR_MODE_DIV_8        0x0000000000000004UL
  42#define ESTAR_MODE_DIV_MASK     0x0000000000000007UL
  43
  44#define MCTRL0_SREFRESH_ENAB    0x0000000000010000UL
  45#define MCTRL0_REFR_COUNT_MASK  0x0000000000007f00UL
  46#define MCTRL0_REFR_COUNT_SHIFT 8
  47#define MCTRL0_REFR_INTERVAL    7800
  48#define MCTRL0_REFR_CLKS_P_CNT  64
  49
  50static unsigned long read_hbreg(unsigned long addr)
  51{
  52        unsigned long ret;
  53
  54        __asm__ __volatile__("ldxa      [%1] %2, %0"
  55                             : "=&r" (ret)
  56                             : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
  57        return ret;
  58}
  59
  60static void write_hbreg(unsigned long addr, unsigned long val)
  61{
  62        __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
  63                             "membar    #Sync"
  64                             : /* no outputs */
  65                             : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
  66                             : "memory");
  67        if (addr == HBIRD_ESTAR_MODE_ADDR) {
  68                /* Need to wait 16 clock cycles for the PLL to lock.  */
  69                udelay(1);
  70        }
  71}
  72
  73static void self_refresh_ctl(int enable)
  74{
  75        unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
  76
  77        if (enable)
  78                mctrl |= MCTRL0_SREFRESH_ENAB;
  79        else
  80                mctrl &= ~MCTRL0_SREFRESH_ENAB;
  81        write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
  82        (void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
  83}
  84
  85static void frob_mem_refresh(int cpu_slowing_down,
  86                             unsigned long clock_tick,
  87                             unsigned long old_divisor, unsigned long divisor)
  88{
  89        unsigned long old_refr_count, refr_count, mctrl;
  90
  91        refr_count  = (clock_tick * MCTRL0_REFR_INTERVAL);
  92        refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
  93
  94        mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
  95        old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
  96                >> MCTRL0_REFR_COUNT_SHIFT;
  97
  98        mctrl &= ~MCTRL0_REFR_COUNT_MASK;
  99        mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
 100        write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
 101        mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
 102
 103        if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
 104                unsigned long usecs;
 105
 106                /* We have to wait for both refresh counts (old
 107                 * and new) to go to zero.
 108                 */
 109                usecs = (MCTRL0_REFR_CLKS_P_CNT *
 110                         (refr_count + old_refr_count) *
 111                         1000000UL *
 112                         old_divisor) / clock_tick;
 113                udelay(usecs + 1UL);
 114        }
 115}
 116
 117static void us2e_transition(unsigned long estar, unsigned long new_bits,
 118                            unsigned long clock_tick,
 119                            unsigned long old_divisor, unsigned long divisor)
 120{
 121        unsigned long flags;
 122
 123        local_irq_save(flags);
 124
 125        estar &= ~ESTAR_MODE_DIV_MASK;
 126
 127        /* This is based upon the state transition diagram in the IIe manual.  */
 128        if (old_divisor == 2 && divisor == 1) {
 129                self_refresh_ctl(0);
 130                write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
 131                frob_mem_refresh(0, clock_tick, old_divisor, divisor);
 132        } else if (old_divisor == 1 && divisor == 2) {
 133                frob_mem_refresh(1, clock_tick, old_divisor, divisor);
 134                write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
 135                self_refresh_ctl(1);
 136        } else if (old_divisor == 1 && divisor > 2) {
 137                us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
 138                                1, 2);
 139                us2e_transition(estar, new_bits, clock_tick,
 140                                2, divisor);
 141        } else if (old_divisor > 2 && divisor == 1) {
 142                us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
 143                                old_divisor, 2);
 144                us2e_transition(estar, new_bits, clock_tick,
 145                                2, divisor);
 146        } else if (old_divisor < divisor) {
 147                frob_mem_refresh(0, clock_tick, old_divisor, divisor);
 148                write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
 149        } else if (old_divisor > divisor) {
 150                write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
 151                frob_mem_refresh(1, clock_tick, old_divisor, divisor);
 152        } else {
 153                BUG();
 154        }
 155
 156        local_irq_restore(flags);
 157}
 158
 159static unsigned long index_to_estar_mode(unsigned int index)
 160{
 161        switch (index) {
 162        case 0:
 163                return ESTAR_MODE_DIV_1;
 164
 165        case 1:
 166                return ESTAR_MODE_DIV_2;
 167
 168        case 2:
 169                return ESTAR_MODE_DIV_4;
 170
 171        case 3:
 172                return ESTAR_MODE_DIV_6;
 173
 174        case 4:
 175                return ESTAR_MODE_DIV_8;
 176
 177        default:
 178                BUG();
 179        }
 180}
 181
 182static unsigned long index_to_divisor(unsigned int index)
 183{
 184        switch (index) {
 185        case 0:
 186                return 1;
 187
 188        case 1:
 189                return 2;
 190
 191        case 2:
 192                return 4;
 193
 194        case 3:
 195                return 6;
 196
 197        case 4:
 198                return 8;
 199
 200        default:
 201                BUG();
 202        }
 203}
 204
 205static unsigned long estar_to_divisor(unsigned long estar)
 206{
 207        unsigned long ret;
 208
 209        switch (estar & ESTAR_MODE_DIV_MASK) {
 210        case ESTAR_MODE_DIV_1:
 211                ret = 1;
 212                break;
 213        case ESTAR_MODE_DIV_2:
 214                ret = 2;
 215                break;
 216        case ESTAR_MODE_DIV_4:
 217                ret = 4;
 218                break;
 219        case ESTAR_MODE_DIV_6:
 220                ret = 6;
 221                break;
 222        case ESTAR_MODE_DIV_8:
 223                ret = 8;
 224                break;
 225        default:
 226                BUG();
 227        }
 228
 229        return ret;
 230}
 231
 232static unsigned int us2e_freq_get(unsigned int cpu)
 233{
 234        cpumask_t cpus_allowed;
 235        unsigned long clock_tick, estar;
 236
 237        cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
 238        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 239
 240        clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 241        estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
 242
 243        set_cpus_allowed_ptr(current, &cpus_allowed);
 244
 245        return clock_tick / estar_to_divisor(estar);
 246}
 247
 248static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index)
 249{
 250        unsigned int cpu = policy->cpu;
 251        unsigned long new_bits, new_freq;
 252        unsigned long clock_tick, divisor, old_divisor, estar;
 253        cpumask_t cpus_allowed;
 254
 255        cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
 256        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 257
 258        new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 259        new_bits = index_to_estar_mode(index);
 260        divisor = index_to_divisor(index);
 261        new_freq /= divisor;
 262
 263        estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
 264
 265        old_divisor = estar_to_divisor(estar);
 266
 267        if (old_divisor != divisor)
 268                us2e_transition(estar, new_bits, clock_tick * 1000,
 269                                old_divisor, divisor);
 270
 271        set_cpus_allowed_ptr(current, &cpus_allowed);
 272
 273        return 0;
 274}
 275
 276static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
 277{
 278        unsigned int cpu = policy->cpu;
 279        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 280        struct cpufreq_frequency_table *table =
 281                &us2e_freq_table[cpu].table[0];
 282
 283        table[0].driver_data = 0;
 284        table[0].frequency = clock_tick / 1;
 285        table[1].driver_data = 1;
 286        table[1].frequency = clock_tick / 2;
 287        table[2].driver_data = 2;
 288        table[2].frequency = clock_tick / 4;
 289        table[2].driver_data = 3;
 290        table[2].frequency = clock_tick / 6;
 291        table[2].driver_data = 4;
 292        table[2].frequency = clock_tick / 8;
 293        table[2].driver_data = 5;
 294        table[3].frequency = CPUFREQ_TABLE_END;
 295
 296        policy->cpuinfo.transition_latency = 0;
 297        policy->cur = clock_tick;
 298
 299        return cpufreq_table_validate_and_show(policy, table);
 300}
 301
 302static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
 303{
 304        if (cpufreq_us2e_driver)
 305                us2e_freq_target(policy, 0);
 306
 307        return 0;
 308}
 309
 310static int __init us2e_freq_init(void)
 311{
 312        unsigned long manuf, impl, ver;
 313        int ret;
 314
 315        if (tlb_type != spitfire)
 316                return -ENODEV;
 317
 318        __asm__("rdpr %%ver, %0" : "=r" (ver));
 319        manuf = ((ver >> 48) & 0xffff);
 320        impl  = ((ver >> 32) & 0xffff);
 321
 322        if (manuf == 0x17 && impl == 0x13) {
 323                struct cpufreq_driver *driver;
 324
 325                ret = -ENOMEM;
 326                driver = kzalloc(sizeof(*driver), GFP_KERNEL);
 327                if (!driver)
 328                        goto err_out;
 329
 330                us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)),
 331                        GFP_KERNEL);
 332                if (!us2e_freq_table)
 333                        goto err_out;
 334
 335                driver->init = us2e_freq_cpu_init;
 336                driver->verify = cpufreq_generic_frequency_table_verify;
 337                driver->target_index = us2e_freq_target;
 338                driver->get = us2e_freq_get;
 339                driver->exit = us2e_freq_cpu_exit;
 340                strcpy(driver->name, "UltraSPARC-IIe");
 341
 342                cpufreq_us2e_driver = driver;
 343                ret = cpufreq_register_driver(driver);
 344                if (ret)
 345                        goto err_out;
 346
 347                return 0;
 348
 349err_out:
 350                if (driver) {
 351                        kfree(driver);
 352                        cpufreq_us2e_driver = NULL;
 353                }
 354                kfree(us2e_freq_table);
 355                us2e_freq_table = NULL;
 356                return ret;
 357        }
 358
 359        return -ENODEV;
 360}
 361
 362static void __exit us2e_freq_exit(void)
 363{
 364        if (cpufreq_us2e_driver) {
 365                cpufreq_unregister_driver(cpufreq_us2e_driver);
 366                kfree(cpufreq_us2e_driver);
 367                cpufreq_us2e_driver = NULL;
 368                kfree(us2e_freq_table);
 369                us2e_freq_table = NULL;
 370        }
 371}
 372
 373MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 374MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
 375MODULE_LICENSE("GPL");
 376
 377module_init(us2e_freq_init);
 378module_exit(us2e_freq_exit);
 379