Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* us3_cpufreq.c: UltraSPARC-III cpu frequency support
0003  *
0004  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
0005  *
0006  * Many thanks to Dominik Brodowski for fixing up the cpufreq
0007  * infrastructure in order to make this driver easier to implement.
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/init.h>
0018 
0019 #include <asm/head.h>
0020 #include <asm/timer.h>
0021 
0022 static struct cpufreq_driver *cpufreq_us3_driver;
0023 
0024 struct us3_freq_percpu_info {
0025     struct cpufreq_frequency_table table[4];
0026 };
0027 
0028 /* Indexed by cpu number. */
0029 static struct us3_freq_percpu_info *us3_freq_table;
0030 
0031 /* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
0032  * in the Safari config register.
0033  */
0034 #define SAFARI_CFG_DIV_1    0x0000000000000000UL
0035 #define SAFARI_CFG_DIV_2    0x0000000040000000UL
0036 #define SAFARI_CFG_DIV_32   0x0000000080000000UL
0037 #define SAFARI_CFG_DIV_MASK 0x00000000C0000000UL
0038 
0039 static void read_safari_cfg(void *arg)
0040 {
0041     unsigned long ret, *val = arg;
0042 
0043     __asm__ __volatile__("ldxa  [%%g0] %1, %0"
0044                  : "=&r" (ret)
0045                  : "i" (ASI_SAFARI_CONFIG));
0046     *val = ret;
0047 }
0048 
0049 static void update_safari_cfg(void *arg)
0050 {
0051     unsigned long reg, *new_bits = arg;
0052 
0053     read_safari_cfg(&reg);
0054     reg &= ~SAFARI_CFG_DIV_MASK;
0055     reg |= *new_bits;
0056 
0057     __asm__ __volatile__("stxa  %0, [%%g0] %1\n\t"
0058                  "membar    #Sync"
0059                  : /* no outputs */
0060                  : "r" (reg), "i" (ASI_SAFARI_CONFIG)
0061                  : "memory");
0062 }
0063 
0064 static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
0065 {
0066     unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
0067     unsigned long ret;
0068 
0069     switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
0070     case SAFARI_CFG_DIV_1:
0071         ret = clock_tick / 1;
0072         break;
0073     case SAFARI_CFG_DIV_2:
0074         ret = clock_tick / 2;
0075         break;
0076     case SAFARI_CFG_DIV_32:
0077         ret = clock_tick / 32;
0078         break;
0079     default:
0080         BUG();
0081     }
0082 
0083     return ret;
0084 }
0085 
0086 static unsigned int us3_freq_get(unsigned int cpu)
0087 {
0088     unsigned long reg;
0089 
0090     if (smp_call_function_single(cpu, read_safari_cfg, &reg, 1))
0091         return 0;
0092     return get_current_freq(cpu, reg);
0093 }
0094 
0095 static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
0096 {
0097     unsigned int cpu = policy->cpu;
0098     unsigned long new_bits, new_freq;
0099 
0100     new_freq = sparc64_get_clock_tick(cpu) / 1000;
0101     switch (index) {
0102     case 0:
0103         new_bits = SAFARI_CFG_DIV_1;
0104         new_freq /= 1;
0105         break;
0106     case 1:
0107         new_bits = SAFARI_CFG_DIV_2;
0108         new_freq /= 2;
0109         break;
0110     case 2:
0111         new_bits = SAFARI_CFG_DIV_32;
0112         new_freq /= 32;
0113         break;
0114 
0115     default:
0116         BUG();
0117     }
0118 
0119     return smp_call_function_single(cpu, update_safari_cfg, &new_bits, 1);
0120 }
0121 
0122 static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
0123 {
0124     unsigned int cpu = policy->cpu;
0125     unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
0126     struct cpufreq_frequency_table *table =
0127         &us3_freq_table[cpu].table[0];
0128 
0129     table[0].driver_data = 0;
0130     table[0].frequency = clock_tick / 1;
0131     table[1].driver_data = 1;
0132     table[1].frequency = clock_tick / 2;
0133     table[2].driver_data = 2;
0134     table[2].frequency = clock_tick / 32;
0135     table[3].driver_data = 0;
0136     table[3].frequency = CPUFREQ_TABLE_END;
0137 
0138     policy->cpuinfo.transition_latency = 0;
0139     policy->cur = clock_tick;
0140     policy->freq_table = table;
0141 
0142     return 0;
0143 }
0144 
0145 static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
0146 {
0147     if (cpufreq_us3_driver)
0148         us3_freq_target(policy, 0);
0149 
0150     return 0;
0151 }
0152 
0153 static int __init us3_freq_init(void)
0154 {
0155     unsigned long manuf, impl, ver;
0156     int ret;
0157 
0158     if (tlb_type != cheetah && tlb_type != cheetah_plus)
0159         return -ENODEV;
0160 
0161     __asm__("rdpr %%ver, %0" : "=r" (ver));
0162     manuf = ((ver >> 48) & 0xffff);
0163     impl  = ((ver >> 32) & 0xffff);
0164 
0165     if (manuf == CHEETAH_MANUF &&
0166         (impl == CHEETAH_IMPL ||
0167          impl == CHEETAH_PLUS_IMPL ||
0168          impl == JAGUAR_IMPL ||
0169          impl == PANTHER_IMPL)) {
0170         struct cpufreq_driver *driver;
0171 
0172         ret = -ENOMEM;
0173         driver = kzalloc(sizeof(*driver), GFP_KERNEL);
0174         if (!driver)
0175             goto err_out;
0176 
0177         us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
0178             GFP_KERNEL);
0179         if (!us3_freq_table)
0180             goto err_out;
0181 
0182         driver->init = us3_freq_cpu_init;
0183         driver->verify = cpufreq_generic_frequency_table_verify;
0184         driver->target_index = us3_freq_target;
0185         driver->get = us3_freq_get;
0186         driver->exit = us3_freq_cpu_exit;
0187         strcpy(driver->name, "UltraSPARC-III");
0188 
0189         cpufreq_us3_driver = driver;
0190         ret = cpufreq_register_driver(driver);
0191         if (ret)
0192             goto err_out;
0193 
0194         return 0;
0195 
0196 err_out:
0197         if (driver) {
0198             kfree(driver);
0199             cpufreq_us3_driver = NULL;
0200         }
0201         kfree(us3_freq_table);
0202         us3_freq_table = NULL;
0203         return ret;
0204     }
0205 
0206     return -ENODEV;
0207 }
0208 
0209 static void __exit us3_freq_exit(void)
0210 {
0211     if (cpufreq_us3_driver) {
0212         cpufreq_unregister_driver(cpufreq_us3_driver);
0213         kfree(cpufreq_us3_driver);
0214         cpufreq_us3_driver = NULL;
0215         kfree(us3_freq_table);
0216         us3_freq_table = NULL;
0217     }
0218 }
0219 
0220 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
0221 MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
0222 MODULE_LICENSE("GPL");
0223 
0224 module_init(us3_freq_init);
0225 module_exit(us3_freq_exit);