root/drivers/cpufreq/sparc-us2e-cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. read_hbreg
  2. write_hbreg
  3. self_refresh_ctl
  4. frob_mem_refresh
  5. us2e_transition
  6. index_to_estar_mode
  7. index_to_divisor
  8. estar_to_divisor
  9. __us2e_freq_get
  10. us2e_freq_get
  11. __us2e_freq_target
  12. us2e_freq_target
  13. us2e_freq_cpu_init
  14. us2e_freq_cpu_exit
  15. us2e_freq_init
  16. us2e_freq_exit

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

/* [<][>][^][v][top][bottom][index][help] */