1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. |
4 | * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. |
5 | * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> |
6 | */ |
7 | /* |
8 | * clock and PLL management functions |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/via-core.h> |
13 | |
14 | #include "via_clock.h" |
15 | #include "global.h" |
16 | #include "debug.h" |
17 | |
18 | static const char *via_slap = "Please slap VIA Technologies to motivate them " |
19 | "releasing full documentation for your platform!\n" ; |
20 | |
21 | static inline u32 cle266_encode_pll(struct via_pll_config pll) |
22 | { |
23 | return (pll.multiplier << 8) |
24 | | (pll.rshift << 6) |
25 | | pll.divisor; |
26 | } |
27 | |
28 | static inline u32 k800_encode_pll(struct via_pll_config pll) |
29 | { |
30 | return ((pll.divisor - 2) << 16) |
31 | | (pll.rshift << 10) |
32 | | (pll.multiplier - 2); |
33 | } |
34 | |
35 | static inline u32 vx855_encode_pll(struct via_pll_config pll) |
36 | { |
37 | return (pll.divisor << 16) |
38 | | (pll.rshift << 10) |
39 | | pll.multiplier; |
40 | } |
41 | |
42 | static inline void cle266_set_primary_pll_encoded(u32 data) |
43 | { |
44 | via_write_reg_mask(VIASR, index: 0x40, data: 0x02, mask: 0x02); /* enable reset */ |
45 | via_write_reg(VIASR, index: 0x46, data: data & 0xFF); |
46 | via_write_reg(VIASR, index: 0x47, data: (data >> 8) & 0xFF); |
47 | via_write_reg_mask(VIASR, index: 0x40, data: 0x00, mask: 0x02); /* disable reset */ |
48 | } |
49 | |
50 | static inline void k800_set_primary_pll_encoded(u32 data) |
51 | { |
52 | via_write_reg_mask(VIASR, index: 0x40, data: 0x02, mask: 0x02); /* enable reset */ |
53 | via_write_reg(VIASR, index: 0x44, data: data & 0xFF); |
54 | via_write_reg(VIASR, index: 0x45, data: (data >> 8) & 0xFF); |
55 | via_write_reg(VIASR, index: 0x46, data: (data >> 16) & 0xFF); |
56 | via_write_reg_mask(VIASR, index: 0x40, data: 0x00, mask: 0x02); /* disable reset */ |
57 | } |
58 | |
59 | static inline void cle266_set_secondary_pll_encoded(u32 data) |
60 | { |
61 | via_write_reg_mask(VIASR, index: 0x40, data: 0x04, mask: 0x04); /* enable reset */ |
62 | via_write_reg(VIASR, index: 0x44, data: data & 0xFF); |
63 | via_write_reg(VIASR, index: 0x45, data: (data >> 8) & 0xFF); |
64 | via_write_reg_mask(VIASR, index: 0x40, data: 0x00, mask: 0x04); /* disable reset */ |
65 | } |
66 | |
67 | static inline void k800_set_secondary_pll_encoded(u32 data) |
68 | { |
69 | via_write_reg_mask(VIASR, index: 0x40, data: 0x04, mask: 0x04); /* enable reset */ |
70 | via_write_reg(VIASR, index: 0x4A, data: data & 0xFF); |
71 | via_write_reg(VIASR, index: 0x4B, data: (data >> 8) & 0xFF); |
72 | via_write_reg(VIASR, index: 0x4C, data: (data >> 16) & 0xFF); |
73 | via_write_reg_mask(VIASR, index: 0x40, data: 0x00, mask: 0x04); /* disable reset */ |
74 | } |
75 | |
76 | static inline void set_engine_pll_encoded(u32 data) |
77 | { |
78 | via_write_reg_mask(VIASR, index: 0x40, data: 0x01, mask: 0x01); /* enable reset */ |
79 | via_write_reg(VIASR, index: 0x47, data: data & 0xFF); |
80 | via_write_reg(VIASR, index: 0x48, data: (data >> 8) & 0xFF); |
81 | via_write_reg(VIASR, index: 0x49, data: (data >> 16) & 0xFF); |
82 | via_write_reg_mask(VIASR, index: 0x40, data: 0x00, mask: 0x01); /* disable reset */ |
83 | } |
84 | |
85 | static void cle266_set_primary_pll(struct via_pll_config config) |
86 | { |
87 | cle266_set_primary_pll_encoded(data: cle266_encode_pll(pll: config)); |
88 | } |
89 | |
90 | static void k800_set_primary_pll(struct via_pll_config config) |
91 | { |
92 | k800_set_primary_pll_encoded(data: k800_encode_pll(pll: config)); |
93 | } |
94 | |
95 | static void vx855_set_primary_pll(struct via_pll_config config) |
96 | { |
97 | k800_set_primary_pll_encoded(data: vx855_encode_pll(pll: config)); |
98 | } |
99 | |
100 | static void cle266_set_secondary_pll(struct via_pll_config config) |
101 | { |
102 | cle266_set_secondary_pll_encoded(data: cle266_encode_pll(pll: config)); |
103 | } |
104 | |
105 | static void k800_set_secondary_pll(struct via_pll_config config) |
106 | { |
107 | k800_set_secondary_pll_encoded(data: k800_encode_pll(pll: config)); |
108 | } |
109 | |
110 | static void vx855_set_secondary_pll(struct via_pll_config config) |
111 | { |
112 | k800_set_secondary_pll_encoded(data: vx855_encode_pll(pll: config)); |
113 | } |
114 | |
115 | static void k800_set_engine_pll(struct via_pll_config config) |
116 | { |
117 | set_engine_pll_encoded(k800_encode_pll(pll: config)); |
118 | } |
119 | |
120 | static void vx855_set_engine_pll(struct via_pll_config config) |
121 | { |
122 | set_engine_pll_encoded(vx855_encode_pll(pll: config)); |
123 | } |
124 | |
125 | static void set_primary_pll_state(u8 state) |
126 | { |
127 | u8 value; |
128 | |
129 | switch (state) { |
130 | case VIA_STATE_ON: |
131 | value = 0x20; |
132 | break; |
133 | case VIA_STATE_OFF: |
134 | value = 0x00; |
135 | break; |
136 | default: |
137 | return; |
138 | } |
139 | |
140 | via_write_reg_mask(VIASR, index: 0x2D, data: value, mask: 0x30); |
141 | } |
142 | |
143 | static void set_secondary_pll_state(u8 state) |
144 | { |
145 | u8 value; |
146 | |
147 | switch (state) { |
148 | case VIA_STATE_ON: |
149 | value = 0x08; |
150 | break; |
151 | case VIA_STATE_OFF: |
152 | value = 0x00; |
153 | break; |
154 | default: |
155 | return; |
156 | } |
157 | |
158 | via_write_reg_mask(VIASR, index: 0x2D, data: value, mask: 0x0C); |
159 | } |
160 | |
161 | static void set_engine_pll_state(u8 state) |
162 | { |
163 | u8 value; |
164 | |
165 | switch (state) { |
166 | case VIA_STATE_ON: |
167 | value = 0x02; |
168 | break; |
169 | case VIA_STATE_OFF: |
170 | value = 0x00; |
171 | break; |
172 | default: |
173 | return; |
174 | } |
175 | |
176 | via_write_reg_mask(VIASR, index: 0x2D, data: value, mask: 0x03); |
177 | } |
178 | |
179 | static void set_primary_clock_state(u8 state) |
180 | { |
181 | u8 value; |
182 | |
183 | switch (state) { |
184 | case VIA_STATE_ON: |
185 | value = 0x20; |
186 | break; |
187 | case VIA_STATE_OFF: |
188 | value = 0x00; |
189 | break; |
190 | default: |
191 | return; |
192 | } |
193 | |
194 | via_write_reg_mask(VIASR, index: 0x1B, data: value, mask: 0x30); |
195 | } |
196 | |
197 | static void set_secondary_clock_state(u8 state) |
198 | { |
199 | u8 value; |
200 | |
201 | switch (state) { |
202 | case VIA_STATE_ON: |
203 | value = 0x80; |
204 | break; |
205 | case VIA_STATE_OFF: |
206 | value = 0x00; |
207 | break; |
208 | default: |
209 | return; |
210 | } |
211 | |
212 | via_write_reg_mask(VIASR, index: 0x1B, data: value, mask: 0xC0); |
213 | } |
214 | |
215 | static inline u8 set_clock_source_common(enum via_clksrc source, bool use_pll) |
216 | { |
217 | u8 data = 0; |
218 | |
219 | switch (source) { |
220 | case VIA_CLKSRC_X1: |
221 | data = 0x00; |
222 | break; |
223 | case VIA_CLKSRC_TVX1: |
224 | data = 0x02; |
225 | break; |
226 | case VIA_CLKSRC_TVPLL: |
227 | data = 0x04; /* 0x06 should be the same */ |
228 | break; |
229 | case VIA_CLKSRC_DVP1TVCLKR: |
230 | data = 0x0A; |
231 | break; |
232 | case VIA_CLKSRC_CAP0: |
233 | data = 0xC; |
234 | break; |
235 | case VIA_CLKSRC_CAP1: |
236 | data = 0x0E; |
237 | break; |
238 | } |
239 | |
240 | if (!use_pll) |
241 | data |= 1; |
242 | |
243 | return data; |
244 | } |
245 | |
246 | static void set_primary_clock_source(enum via_clksrc source, bool use_pll) |
247 | { |
248 | u8 data = set_clock_source_common(source, use_pll) << 4; |
249 | via_write_reg_mask(VIACR, index: 0x6C, data, mask: 0xF0); |
250 | } |
251 | |
252 | static void set_secondary_clock_source(enum via_clksrc source, bool use_pll) |
253 | { |
254 | u8 data = set_clock_source_common(source, use_pll); |
255 | via_write_reg_mask(VIACR, index: 0x6C, data, mask: 0x0F); |
256 | } |
257 | |
258 | static void dummy_set_clock_state(u8 state) |
259 | { |
260 | printk(KERN_INFO "Using undocumented set clock state.\n%s" , via_slap); |
261 | } |
262 | |
263 | static void dummy_set_clock_source(enum via_clksrc source, bool use_pll) |
264 | { |
265 | printk(KERN_INFO "Using undocumented set clock source.\n%s" , via_slap); |
266 | } |
267 | |
268 | static void dummy_set_pll_state(u8 state) |
269 | { |
270 | printk(KERN_INFO "Using undocumented set PLL state.\n%s" , via_slap); |
271 | } |
272 | |
273 | static void dummy_set_pll(struct via_pll_config config) |
274 | { |
275 | printk(KERN_INFO "Using undocumented set PLL.\n%s" , via_slap); |
276 | } |
277 | |
278 | static void noop_set_clock_state(u8 state) |
279 | { |
280 | } |
281 | |
282 | void via_clock_init(struct via_clock *clock, int gfx_chip) |
283 | { |
284 | switch (gfx_chip) { |
285 | case UNICHROME_CLE266: |
286 | case UNICHROME_K400: |
287 | clock->set_primary_clock_state = dummy_set_clock_state; |
288 | clock->set_primary_clock_source = dummy_set_clock_source; |
289 | clock->set_primary_pll_state = dummy_set_pll_state; |
290 | clock->set_primary_pll = cle266_set_primary_pll; |
291 | |
292 | clock->set_secondary_clock_state = dummy_set_clock_state; |
293 | clock->set_secondary_clock_source = dummy_set_clock_source; |
294 | clock->set_secondary_pll_state = dummy_set_pll_state; |
295 | clock->set_secondary_pll = cle266_set_secondary_pll; |
296 | |
297 | clock->set_engine_pll_state = dummy_set_pll_state; |
298 | clock->set_engine_pll = dummy_set_pll; |
299 | break; |
300 | case UNICHROME_K800: |
301 | case UNICHROME_PM800: |
302 | case UNICHROME_CN700: |
303 | case UNICHROME_CX700: |
304 | case UNICHROME_CN750: |
305 | case UNICHROME_K8M890: |
306 | case UNICHROME_P4M890: |
307 | case UNICHROME_P4M900: |
308 | case UNICHROME_VX800: |
309 | clock->set_primary_clock_state = set_primary_clock_state; |
310 | clock->set_primary_clock_source = set_primary_clock_source; |
311 | clock->set_primary_pll_state = set_primary_pll_state; |
312 | clock->set_primary_pll = k800_set_primary_pll; |
313 | |
314 | clock->set_secondary_clock_state = set_secondary_clock_state; |
315 | clock->set_secondary_clock_source = set_secondary_clock_source; |
316 | clock->set_secondary_pll_state = set_secondary_pll_state; |
317 | clock->set_secondary_pll = k800_set_secondary_pll; |
318 | |
319 | clock->set_engine_pll_state = set_engine_pll_state; |
320 | clock->set_engine_pll = k800_set_engine_pll; |
321 | break; |
322 | case UNICHROME_VX855: |
323 | case UNICHROME_VX900: |
324 | clock->set_primary_clock_state = set_primary_clock_state; |
325 | clock->set_primary_clock_source = set_primary_clock_source; |
326 | clock->set_primary_pll_state = set_primary_pll_state; |
327 | clock->set_primary_pll = vx855_set_primary_pll; |
328 | |
329 | clock->set_secondary_clock_state = set_secondary_clock_state; |
330 | clock->set_secondary_clock_source = set_secondary_clock_source; |
331 | clock->set_secondary_pll_state = set_secondary_pll_state; |
332 | clock->set_secondary_pll = vx855_set_secondary_pll; |
333 | |
334 | clock->set_engine_pll_state = set_engine_pll_state; |
335 | clock->set_engine_pll = vx855_set_engine_pll; |
336 | break; |
337 | |
338 | } |
339 | |
340 | if (machine_is_olpc()) { |
341 | /* The OLPC XO-1.5 cannot suspend/resume reliably if the |
342 | * IGA1/IGA2 clocks are set as on or off (memory rot |
343 | * occasionally happens during suspend under such |
344 | * configurations). |
345 | * |
346 | * The only known stable scenario is to leave this bits as-is, |
347 | * which in their default states are documented to enable the |
348 | * clock only when it is needed. |
349 | */ |
350 | clock->set_primary_clock_state = noop_set_clock_state; |
351 | clock->set_secondary_clock_state = noop_set_clock_state; |
352 | } |
353 | } |
354 | |