0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/sched.h>
0013 #include <linux/smp.h>
0014 #include <linux/cpufreq.h>
0015 #include <linux/threads.h>
0016 #include <linux/slab.h>
0017 #include <linux/delay.h>
0018 #include <linux/init.h>
0019
0020 #include <asm/asi.h>
0021 #include <asm/timer.h>
0022
0023 static struct cpufreq_driver *cpufreq_us2e_driver;
0024
0025 struct us2e_freq_percpu_info {
0026 struct cpufreq_frequency_table table[6];
0027 };
0028
0029
0030 static struct us2e_freq_percpu_info *us2e_freq_table;
0031
0032 #define HBIRD_MEM_CNTL0_ADDR 0x1fe0000f010UL
0033 #define HBIRD_ESTAR_MODE_ADDR 0x1fe0000f080UL
0034
0035
0036
0037
0038 #define ESTAR_MODE_DIV_1 0x0000000000000000UL
0039 #define ESTAR_MODE_DIV_2 0x0000000000000001UL
0040 #define ESTAR_MODE_DIV_4 0x0000000000000003UL
0041 #define ESTAR_MODE_DIV_6 0x0000000000000002UL
0042 #define ESTAR_MODE_DIV_8 0x0000000000000004UL
0043 #define ESTAR_MODE_DIV_MASK 0x0000000000000007UL
0044
0045 #define MCTRL0_SREFRESH_ENAB 0x0000000000010000UL
0046 #define MCTRL0_REFR_COUNT_MASK 0x0000000000007f00UL
0047 #define MCTRL0_REFR_COUNT_SHIFT 8
0048 #define MCTRL0_REFR_INTERVAL 7800
0049 #define MCTRL0_REFR_CLKS_P_CNT 64
0050
0051 static unsigned long read_hbreg(unsigned long addr)
0052 {
0053 unsigned long ret;
0054
0055 __asm__ __volatile__("ldxa [%1] %2, %0"
0056 : "=&r" (ret)
0057 : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
0058 return ret;
0059 }
0060
0061 static void write_hbreg(unsigned long addr, unsigned long val)
0062 {
0063 __asm__ __volatile__("stxa %0, [%1] %2\n\t"
0064 "membar #Sync"
0065 :
0066 : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
0067 : "memory");
0068 if (addr == HBIRD_ESTAR_MODE_ADDR) {
0069
0070 udelay(1);
0071 }
0072 }
0073
0074 static void self_refresh_ctl(int enable)
0075 {
0076 unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
0077
0078 if (enable)
0079 mctrl |= MCTRL0_SREFRESH_ENAB;
0080 else
0081 mctrl &= ~MCTRL0_SREFRESH_ENAB;
0082 write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
0083 (void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
0084 }
0085
0086 static void frob_mem_refresh(int cpu_slowing_down,
0087 unsigned long clock_tick,
0088 unsigned long old_divisor, unsigned long divisor)
0089 {
0090 unsigned long old_refr_count, refr_count, mctrl;
0091
0092 refr_count = (clock_tick * MCTRL0_REFR_INTERVAL);
0093 refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
0094
0095 mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
0096 old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
0097 >> MCTRL0_REFR_COUNT_SHIFT;
0098
0099 mctrl &= ~MCTRL0_REFR_COUNT_MASK;
0100 mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
0101 write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
0102 mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
0103
0104 if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
0105 unsigned long usecs;
0106
0107
0108
0109
0110 usecs = (MCTRL0_REFR_CLKS_P_CNT *
0111 (refr_count + old_refr_count) *
0112 1000000UL *
0113 old_divisor) / clock_tick;
0114 udelay(usecs + 1UL);
0115 }
0116 }
0117
0118 static void us2e_transition(unsigned long estar, unsigned long new_bits,
0119 unsigned long clock_tick,
0120 unsigned long old_divisor, unsigned long divisor)
0121 {
0122 estar &= ~ESTAR_MODE_DIV_MASK;
0123
0124
0125 if (old_divisor == 2 && divisor == 1) {
0126 self_refresh_ctl(0);
0127 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
0128 frob_mem_refresh(0, clock_tick, old_divisor, divisor);
0129 } else if (old_divisor == 1 && divisor == 2) {
0130 frob_mem_refresh(1, clock_tick, old_divisor, divisor);
0131 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
0132 self_refresh_ctl(1);
0133 } else if (old_divisor == 1 && divisor > 2) {
0134 us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
0135 1, 2);
0136 us2e_transition(estar, new_bits, clock_tick,
0137 2, divisor);
0138 } else if (old_divisor > 2 && divisor == 1) {
0139 us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
0140 old_divisor, 2);
0141 us2e_transition(estar, new_bits, clock_tick,
0142 2, divisor);
0143 } else if (old_divisor < divisor) {
0144 frob_mem_refresh(0, clock_tick, old_divisor, divisor);
0145 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
0146 } else if (old_divisor > divisor) {
0147 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
0148 frob_mem_refresh(1, clock_tick, old_divisor, divisor);
0149 } else {
0150 BUG();
0151 }
0152 }
0153
0154 static unsigned long index_to_estar_mode(unsigned int index)
0155 {
0156 switch (index) {
0157 case 0:
0158 return ESTAR_MODE_DIV_1;
0159
0160 case 1:
0161 return ESTAR_MODE_DIV_2;
0162
0163 case 2:
0164 return ESTAR_MODE_DIV_4;
0165
0166 case 3:
0167 return ESTAR_MODE_DIV_6;
0168
0169 case 4:
0170 return ESTAR_MODE_DIV_8;
0171
0172 default:
0173 BUG();
0174 }
0175 }
0176
0177 static unsigned long index_to_divisor(unsigned int index)
0178 {
0179 switch (index) {
0180 case 0:
0181 return 1;
0182
0183 case 1:
0184 return 2;
0185
0186 case 2:
0187 return 4;
0188
0189 case 3:
0190 return 6;
0191
0192 case 4:
0193 return 8;
0194
0195 default:
0196 BUG();
0197 }
0198 }
0199
0200 static unsigned long estar_to_divisor(unsigned long estar)
0201 {
0202 unsigned long ret;
0203
0204 switch (estar & ESTAR_MODE_DIV_MASK) {
0205 case ESTAR_MODE_DIV_1:
0206 ret = 1;
0207 break;
0208 case ESTAR_MODE_DIV_2:
0209 ret = 2;
0210 break;
0211 case ESTAR_MODE_DIV_4:
0212 ret = 4;
0213 break;
0214 case ESTAR_MODE_DIV_6:
0215 ret = 6;
0216 break;
0217 case ESTAR_MODE_DIV_8:
0218 ret = 8;
0219 break;
0220 default:
0221 BUG();
0222 }
0223
0224 return ret;
0225 }
0226
0227 static void __us2e_freq_get(void *arg)
0228 {
0229 unsigned long *estar = arg;
0230
0231 *estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
0232 }
0233
0234 static unsigned int us2e_freq_get(unsigned int cpu)
0235 {
0236 unsigned long clock_tick, estar;
0237
0238 clock_tick = sparc64_get_clock_tick(cpu) / 1000;
0239 if (smp_call_function_single(cpu, __us2e_freq_get, &estar, 1))
0240 return 0;
0241
0242 return clock_tick / estar_to_divisor(estar);
0243 }
0244
0245 static void __us2e_freq_target(void *arg)
0246 {
0247 unsigned int cpu = smp_processor_id();
0248 unsigned int *index = arg;
0249 unsigned long new_bits, new_freq;
0250 unsigned long clock_tick, divisor, old_divisor, estar;
0251
0252 new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
0253 new_bits = index_to_estar_mode(*index);
0254 divisor = index_to_divisor(*index);
0255 new_freq /= divisor;
0256
0257 estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
0258
0259 old_divisor = estar_to_divisor(estar);
0260
0261 if (old_divisor != divisor) {
0262 us2e_transition(estar, new_bits, clock_tick * 1000,
0263 old_divisor, divisor);
0264 }
0265 }
0266
0267 static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index)
0268 {
0269 unsigned int cpu = policy->cpu;
0270
0271 return smp_call_function_single(cpu, __us2e_freq_target, &index, 1);
0272 }
0273
0274 static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
0275 {
0276 unsigned int cpu = policy->cpu;
0277 unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
0278 struct cpufreq_frequency_table *table =
0279 &us2e_freq_table[cpu].table[0];
0280
0281 table[0].driver_data = 0;
0282 table[0].frequency = clock_tick / 1;
0283 table[1].driver_data = 1;
0284 table[1].frequency = clock_tick / 2;
0285 table[2].driver_data = 2;
0286 table[2].frequency = clock_tick / 4;
0287 table[2].driver_data = 3;
0288 table[2].frequency = clock_tick / 6;
0289 table[2].driver_data = 4;
0290 table[2].frequency = clock_tick / 8;
0291 table[2].driver_data = 5;
0292 table[3].frequency = CPUFREQ_TABLE_END;
0293
0294 policy->cpuinfo.transition_latency = 0;
0295 policy->cur = clock_tick;
0296 policy->freq_table = table;
0297
0298 return 0;
0299 }
0300
0301 static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
0302 {
0303 if (cpufreq_us2e_driver)
0304 us2e_freq_target(policy, 0);
0305
0306 return 0;
0307 }
0308
0309 static int __init us2e_freq_init(void)
0310 {
0311 unsigned long manuf, impl, ver;
0312 int ret;
0313
0314 if (tlb_type != spitfire)
0315 return -ENODEV;
0316
0317 __asm__("rdpr %%ver, %0" : "=r" (ver));
0318 manuf = ((ver >> 48) & 0xffff);
0319 impl = ((ver >> 32) & 0xffff);
0320
0321 if (manuf == 0x17 && impl == 0x13) {
0322 struct cpufreq_driver *driver;
0323
0324 ret = -ENOMEM;
0325 driver = kzalloc(sizeof(*driver), GFP_KERNEL);
0326 if (!driver)
0327 goto err_out;
0328
0329 us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)),
0330 GFP_KERNEL);
0331 if (!us2e_freq_table)
0332 goto err_out;
0333
0334 driver->init = us2e_freq_cpu_init;
0335 driver->verify = cpufreq_generic_frequency_table_verify;
0336 driver->target_index = us2e_freq_target;
0337 driver->get = us2e_freq_get;
0338 driver->exit = us2e_freq_cpu_exit;
0339 strcpy(driver->name, "UltraSPARC-IIe");
0340
0341 cpufreq_us2e_driver = driver;
0342 ret = cpufreq_register_driver(driver);
0343 if (ret)
0344 goto err_out;
0345
0346 return 0;
0347
0348 err_out:
0349 if (driver) {
0350 kfree(driver);
0351 cpufreq_us2e_driver = NULL;
0352 }
0353 kfree(us2e_freq_table);
0354 us2e_freq_table = NULL;
0355 return ret;
0356 }
0357
0358 return -ENODEV;
0359 }
0360
0361 static void __exit us2e_freq_exit(void)
0362 {
0363 if (cpufreq_us2e_driver) {
0364 cpufreq_unregister_driver(cpufreq_us2e_driver);
0365 kfree(cpufreq_us2e_driver);
0366 cpufreq_us2e_driver = NULL;
0367 kfree(us2e_freq_table);
0368 us2e_freq_table = NULL;
0369 }
0370 }
0371
0372 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
0373 MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
0374 MODULE_LICENSE("GPL");
0375
0376 module_init(us2e_freq_init);
0377 module_exit(us2e_freq_exit);