Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / cpufreq / sparc-us3-cpufreq.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* us3_cpufreq.c: UltraSPARC-III 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/init.h>
18
19 #include <asm/head.h>
20 #include <asm/timer.h>
21
22 struct us3_freq_percpu_info {
23         struct cpufreq_frequency_table table[4];
24 };
25
26 /* Indexed by cpu number. */
27 static struct us3_freq_percpu_info *us3_freq_table;
28
29 /* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
30  * in the Safari config register.
31  */
32 #define SAFARI_CFG_DIV_1        0x0000000000000000UL
33 #define SAFARI_CFG_DIV_2        0x0000000040000000UL
34 #define SAFARI_CFG_DIV_32       0x0000000080000000UL
35 #define SAFARI_CFG_DIV_MASK     0x00000000C0000000UL
36
37 static void read_safari_cfg(void *arg)
38 {
39         unsigned long ret, *val = arg;
40
41         __asm__ __volatile__("ldxa      [%%g0] %1, %0"
42                              : "=&r" (ret)
43                              : "i" (ASI_SAFARI_CONFIG));
44         *val = ret;
45 }
46
47 static void update_safari_cfg(void *arg)
48 {
49         unsigned long reg, *new_bits = arg;
50
51         read_safari_cfg(&reg);
52         reg &= ~SAFARI_CFG_DIV_MASK;
53         reg |= *new_bits;
54
55         __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
56                              "membar    #Sync"
57                              : /* no outputs */
58                              : "r" (reg), "i" (ASI_SAFARI_CONFIG)
59                              : "memory");
60 }
61
62 static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
63 {
64         unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
65         unsigned long ret;
66
67         switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
68         case SAFARI_CFG_DIV_1:
69                 ret = clock_tick / 1;
70                 break;
71         case SAFARI_CFG_DIV_2:
72                 ret = clock_tick / 2;
73                 break;
74         case SAFARI_CFG_DIV_32:
75                 ret = clock_tick / 32;
76                 break;
77         default:
78                 BUG();
79         }
80
81         return ret;
82 }
83
84 static unsigned int us3_freq_get(unsigned int cpu)
85 {
86         unsigned long reg;
87
88         if (smp_call_function_single(cpu, read_safari_cfg, &reg, 1))
89                 return 0;
90         return get_current_freq(cpu, reg);
91 }
92
93 static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
94 {
95         unsigned int cpu = policy->cpu;
96         unsigned long new_bits, new_freq;
97
98         new_freq = sparc64_get_clock_tick(cpu) / 1000;
99         switch (index) {
100         case 0:
101                 new_bits = SAFARI_CFG_DIV_1;
102                 new_freq /= 1;
103                 break;
104         case 1:
105                 new_bits = SAFARI_CFG_DIV_2;
106                 new_freq /= 2;
107                 break;
108         case 2:
109                 new_bits = SAFARI_CFG_DIV_32;
110                 new_freq /= 32;
111                 break;
112
113         default:
114                 BUG();
115         }
116
117         return smp_call_function_single(cpu, update_safari_cfg, &new_bits, 1);
118 }
119
120 static int us3_freq_cpu_init(struct cpufreq_policy *policy)
121 {
122         unsigned int cpu = policy->cpu;
123         unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
124         struct cpufreq_frequency_table *table =
125                 &us3_freq_table[cpu].table[0];
126
127         table[0].driver_data = 0;
128         table[0].frequency = clock_tick / 1;
129         table[1].driver_data = 1;
130         table[1].frequency = clock_tick / 2;
131         table[2].driver_data = 2;
132         table[2].frequency = clock_tick / 32;
133         table[3].driver_data = 0;
134         table[3].frequency = CPUFREQ_TABLE_END;
135
136         policy->cpuinfo.transition_latency = 0;
137         policy->cur = clock_tick;
138         policy->freq_table = table;
139
140         return 0;
141 }
142
143 static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
144 {
145         us3_freq_target(policy, 0);
146         return 0;
147 }
148
149 static struct cpufreq_driver cpufreq_us3_driver = {
150         .name = "UltraSPARC-III",
151         .init = us3_freq_cpu_init,
152         .verify = cpufreq_generic_frequency_table_verify,
153         .target_index = us3_freq_target,
154         .get = us3_freq_get,
155         .exit = us3_freq_cpu_exit,
156 };
157
158 static int __init us3_freq_init(void)
159 {
160         unsigned long manuf, impl, ver;
161         int ret;
162
163         if (tlb_type != cheetah && tlb_type != cheetah_plus)
164                 return -ENODEV;
165
166         __asm__("rdpr %%ver, %0" : "=r" (ver));
167         manuf = ((ver >> 48) & 0xffff);
168         impl  = ((ver >> 32) & 0xffff);
169
170         if (manuf == CHEETAH_MANUF &&
171             (impl == CHEETAH_IMPL ||
172              impl == CHEETAH_PLUS_IMPL ||
173              impl == JAGUAR_IMPL ||
174              impl == PANTHER_IMPL)) {
175                 us3_freq_table = kzalloc(NR_CPUS * sizeof(*us3_freq_table),
176                                          GFP_KERNEL);
177                 if (!us3_freq_table)
178                         return -ENOMEM;
179
180                 ret = cpufreq_register_driver(&cpufreq_us3_driver);
181                 if (ret)
182                         kfree(us3_freq_table);
183
184                 return ret;
185         }
186
187         return -ENODEV;
188 }
189
190 static void __exit us3_freq_exit(void)
191 {
192         cpufreq_unregister_driver(&cpufreq_us3_driver);
193         kfree(us3_freq_table);
194 }
195
196 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
197 MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
198 MODULE_LICENSE("GPL");
199
200 module_init(us3_freq_init);
201 module_exit(us3_freq_exit);