sparc-us2e-cpufreq.c (8975B)
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 23static struct cpufreq_driver *cpufreq_us2e_driver; 24 25struct us2e_freq_percpu_info { 26 struct cpufreq_frequency_table table[6]; 27}; 28 29/* Indexed by cpu number. */ 30static 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 51static 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 61static 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 74static 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 86static 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 118static 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 154static 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 177static 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 200static 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 227static void __us2e_freq_get(void *arg) 228{ 229 unsigned long *estar = arg; 230 231 *estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR); 232} 233 234static 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 245static 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 267static 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 274static 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 301static 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 309static 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 348err_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 361static 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 372MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); 373MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe"); 374MODULE_LICENSE("GPL"); 375 376module_init(us2e_freq_init); 377module_exit(us2e_freq_exit);