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(arg: ®); |
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(cpuid: cpu, func: read_safari_cfg, info: ®, wait: 1)) |
89 | return 0; |
90 | return get_current_freq(cpu, safari_cfg: 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(cpuid: cpu, func: update_safari_cfg, info: &new_bits, wait: 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, index: 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(driver_data: &cpufreq_us3_driver); |
181 | if (ret) |
182 | kfree(objp: 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(driver_data: &cpufreq_us3_driver); |
193 | kfree(objp: 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); |
202 | |