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