1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * AM33XX CM functions
4 *
5 * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/
6 * Vaibhav Hiremath <hvaibhav@ti.com>
7 *
8 * Reference taken from OMAP4 cminst44xx.c
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/types.h>
13 #include <linux/errno.h>
14 #include <linux/err.h>
15 #include <linux/io.h>
16
17 #include "clockdomain.h"
18 #include "cm.h"
19 #include "cm33xx.h"
20 #include "cm-regbits-34xx.h"
21 #include "cm-regbits-33xx.h"
22 #include "prm33xx.h"
23 #if IS_ENABLED(CONFIG_SUSPEND)
24 #include <linux/suspend.h>
25 #endif
26
27 /*
28 * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
29 *
30 * 0x0 func: Module is fully functional, including OCP
31 * 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep
32 * abortion
33 * 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
34 * using separate functional clock
35 * 0x3 disabled: Module is disabled and cannot be accessed
36 *
37 */
38 #define CLKCTRL_IDLEST_FUNCTIONAL 0x0
39 #define CLKCTRL_IDLEST_INTRANSITION 0x1
40 #define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
41 #define CLKCTRL_IDLEST_DISABLED 0x3
42
43 /* Private functions */
44
45 /* Read a register in a CM instance */
am33xx_cm_read_reg(u16 inst,u16 idx)46 static inline u32 am33xx_cm_read_reg(u16 inst, u16 idx)
47 {
48 return readl_relaxed(cm_base.va + inst + idx);
49 }
50
51 /* Write into a register in a CM */
am33xx_cm_write_reg(u32 val,u16 inst,u16 idx)52 static inline void am33xx_cm_write_reg(u32 val, u16 inst, u16 idx)
53 {
54 writel_relaxed(val, cm_base.va + inst + idx);
55 }
56
57 /* Read-modify-write a register in CM */
am33xx_cm_rmw_reg_bits(u32 mask,u32 bits,s16 inst,s16 idx)58 static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx)
59 {
60 u32 v;
61
62 v = am33xx_cm_read_reg(inst, idx);
63 v &= ~mask;
64 v |= bits;
65 am33xx_cm_write_reg(v, inst, idx);
66
67 return v;
68 }
69
am33xx_cm_read_reg_bits(u16 inst,s16 idx,u32 mask)70 static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask)
71 {
72 u32 v;
73
74 v = am33xx_cm_read_reg(inst, idx);
75 v &= mask;
76 v >>= __ffs(mask);
77
78 return v;
79 }
80
81 /**
82 * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
83 * @inst: CM instance register offset (*_INST macro)
84 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
85 *
86 * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
87 * bit 0.
88 */
_clkctrl_idlest(u16 inst,u16 clkctrl_offs)89 static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs)
90 {
91 u32 v = am33xx_cm_read_reg(inst, clkctrl_offs);
92 v &= AM33XX_IDLEST_MASK;
93 v >>= AM33XX_IDLEST_SHIFT;
94 return v;
95 }
96
97 /**
98 * _is_module_ready - can module registers be accessed without causing an abort?
99 * @inst: CM instance register offset (*_INST macro)
100 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
101 *
102 * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
103 * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
104 */
_is_module_ready(u16 inst,u16 clkctrl_offs)105 static bool _is_module_ready(u16 inst, u16 clkctrl_offs)
106 {
107 u32 v;
108
109 v = _clkctrl_idlest(inst, clkctrl_offs);
110
111 return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
112 v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
113 }
114
115 /**
116 * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield
117 * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted)
118 * @inst: CM instance register offset (*_INST macro)
119 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
120 *
121 * @c must be the unshifted value for CLKTRCTRL - i.e., this function
122 * will handle the shift itself.
123 */
_clktrctrl_write(u8 c,u16 inst,u16 cdoffs)124 static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs)
125 {
126 u32 v;
127
128 v = am33xx_cm_read_reg(inst, cdoffs);
129 v &= ~AM33XX_CLKTRCTRL_MASK;
130 v |= c << AM33XX_CLKTRCTRL_SHIFT;
131 am33xx_cm_write_reg(v, inst, cdoffs);
132 }
133
134 /* Public functions */
135
136 /**
137 * am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode?
138 * @inst: CM instance register offset (*_INST macro)
139 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
140 *
141 * Returns true if the clockdomain referred to by (@inst, @cdoffs)
142 * is in hardware-supervised idle mode, or 0 otherwise.
143 */
am33xx_cm_is_clkdm_in_hwsup(u16 inst,u16 cdoffs)144 static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs)
145 {
146 u32 v;
147
148 v = am33xx_cm_read_reg(inst, cdoffs);
149 v &= AM33XX_CLKTRCTRL_MASK;
150 v >>= AM33XX_CLKTRCTRL_SHIFT;
151
152 return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false;
153 }
154
155 /**
156 * am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode
157 * @inst: CM instance register offset (*_INST macro)
158 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
159 *
160 * Put a clockdomain referred to by (@inst, @cdoffs) into
161 * hardware-supervised idle mode. No return value.
162 */
am33xx_cm_clkdm_enable_hwsup(u16 inst,u16 cdoffs)163 static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs)
164 {
165 _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs);
166 }
167
168 /**
169 * am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode
170 * @inst: CM instance register offset (*_INST macro)
171 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
172 *
173 * Put a clockdomain referred to by (@inst, @cdoffs) into
174 * software-supervised idle mode, i.e., controlled manually by the
175 * Linux OMAP clockdomain code. No return value.
176 */
am33xx_cm_clkdm_disable_hwsup(u16 inst,u16 cdoffs)177 static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs)
178 {
179 _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs);
180 }
181
182 /**
183 * am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle
184 * @inst: CM instance register offset (*_INST macro)
185 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
186 *
187 * Put a clockdomain referred to by (@inst, @cdoffs) into idle
188 * No return value.
189 */
am33xx_cm_clkdm_force_sleep(u16 inst,u16 cdoffs)190 static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs)
191 {
192 _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs);
193 }
194
195 /**
196 * am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle
197 * @inst: CM instance register offset (*_INST macro)
198 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
199 *
200 * Take a clockdomain referred to by (@inst, @cdoffs) out of idle,
201 * waking it up. No return value.
202 */
am33xx_cm_clkdm_force_wakeup(u16 inst,u16 cdoffs)203 static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs)
204 {
205 _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs);
206 }
207
208 /*
209 *
210 */
211
212 /**
213 * am33xx_cm_wait_module_ready - wait for a module to be in 'func' state
214 * @part: PRCM partition, ignored for AM33xx
215 * @inst: CM instance register offset (*_INST macro)
216 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
217 * @bit_shift: bit shift for the register, ignored for AM33xx
218 *
219 * Wait for the module IDLEST to be functional. If the idle state is in any
220 * the non functional state (trans, idle or disabled), module and thus the
221 * sysconfig cannot be accessed and will probably lead to an "imprecise
222 * external abort"
223 */
am33xx_cm_wait_module_ready(u8 part,s16 inst,u16 clkctrl_offs,u8 bit_shift)224 static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs,
225 u8 bit_shift)
226 {
227 int i = 0;
228
229 omap_test_timeout(_is_module_ready(inst, clkctrl_offs),
230 MAX_MODULE_READY_TIME, i);
231
232 return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
233 }
234
235 /**
236 * am33xx_cm_wait_module_idle - wait for a module to be in 'disabled'
237 * state
238 * @part: CM partition, ignored for AM33xx
239 * @inst: CM instance register offset (*_INST macro)
240 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
241 * @bit_shift: bit shift for the register, ignored for AM33xx
242 *
243 * Wait for the module IDLEST to be disabled. Some PRCM transition,
244 * like reset assertion or parent clock de-activation must wait the
245 * module to be fully disabled.
246 */
am33xx_cm_wait_module_idle(u8 part,s16 inst,u16 clkctrl_offs,u8 bit_shift)247 static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs,
248 u8 bit_shift)
249 {
250 int i = 0;
251
252 omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) ==
253 CLKCTRL_IDLEST_DISABLED),
254 MAX_MODULE_READY_TIME, i);
255
256 return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
257 }
258
259 /**
260 * am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL
261 * @mode: Module mode (SW or HW)
262 * @part: CM partition, ignored for AM33xx
263 * @inst: CM instance register offset (*_INST macro)
264 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
265 *
266 * No return value.
267 */
am33xx_cm_module_enable(u8 mode,u8 part,u16 inst,u16 clkctrl_offs)268 static void am33xx_cm_module_enable(u8 mode, u8 part, u16 inst,
269 u16 clkctrl_offs)
270 {
271 u32 v;
272
273 v = am33xx_cm_read_reg(inst, clkctrl_offs);
274 v &= ~AM33XX_MODULEMODE_MASK;
275 v |= mode << AM33XX_MODULEMODE_SHIFT;
276 am33xx_cm_write_reg(v, inst, clkctrl_offs);
277 }
278
279 /**
280 * am33xx_cm_module_disable - Disable the module inside CLKCTRL
281 * @part: CM partition, ignored for AM33xx
282 * @inst: CM instance register offset (*_INST macro)
283 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
284 *
285 * No return value.
286 */
am33xx_cm_module_disable(u8 part,u16 inst,u16 clkctrl_offs)287 static void am33xx_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs)
288 {
289 u32 v;
290
291 v = am33xx_cm_read_reg(inst, clkctrl_offs);
292 v &= ~AM33XX_MODULEMODE_MASK;
293 am33xx_cm_write_reg(v, inst, clkctrl_offs);
294 }
295
296 /*
297 * Clockdomain low-level functions
298 */
299
am33xx_clkdm_sleep(struct clockdomain * clkdm)300 static int am33xx_clkdm_sleep(struct clockdomain *clkdm)
301 {
302 am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs);
303 return 0;
304 }
305
am33xx_clkdm_wakeup(struct clockdomain * clkdm)306 static int am33xx_clkdm_wakeup(struct clockdomain *clkdm)
307 {
308 am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs);
309 return 0;
310 }
311
am33xx_clkdm_allow_idle(struct clockdomain * clkdm)312 static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
313 {
314 am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
315 }
316
am33xx_clkdm_deny_idle(struct clockdomain * clkdm)317 static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
318 {
319 am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
320 }
321
am33xx_clkdm_clk_enable(struct clockdomain * clkdm)322 static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm)
323 {
324 if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
325 return am33xx_clkdm_wakeup(clkdm);
326
327 return 0;
328 }
329
am33xx_clkdm_clk_disable(struct clockdomain * clkdm)330 static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm)
331 {
332 bool hwsup = false;
333
334 #if IS_ENABLED(CONFIG_SUSPEND)
335 /*
336 * In case of standby, Don't put the l4ls clk domain to sleep.
337 * Since CM3 PM FW doesn't wake-up/enable the l4ls clk domain
338 * upon wake-up, CM3 PM FW fails to wake-up th MPU.
339 */
340 if (pm_suspend_target_state == PM_SUSPEND_STANDBY &&
341 (clkdm->flags & CLKDM_STANDBY_FORCE_WAKEUP))
342 return 0;
343 #endif
344 hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
345 if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
346 am33xx_clkdm_sleep(clkdm);
347
348 return 0;
349 }
350
am33xx_cm_xlate_clkctrl(u8 part,u16 inst,u16 offset)351 static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset)
352 {
353 return cm_base.pa + inst + offset;
354 }
355
356 /**
357 * am33xx_clkdm_save_context - Save the clockdomain transition context
358 * @clkdm: The clockdomain pointer whose context needs to be saved
359 *
360 * Save the clockdomain transition context.
361 */
am33xx_clkdm_save_context(struct clockdomain * clkdm)362 static int am33xx_clkdm_save_context(struct clockdomain *clkdm)
363 {
364 clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst,
365 clkdm->clkdm_offs,
366 AM33XX_CLKTRCTRL_MASK);
367
368 return 0;
369 }
370
371 /**
372 * am33xx_clkdm_restore_context - Restore the clockdomain transition context
373 * @clkdm: The clockdomain pointer whose context needs to be restored
374 *
375 * Restore the clockdomain transition context.
376 */
am33xx_clkdm_restore_context(struct clockdomain * clkdm)377 static int am33xx_clkdm_restore_context(struct clockdomain *clkdm)
378 {
379 switch (clkdm->context) {
380 case OMAP34XX_CLKSTCTRL_DISABLE_AUTO:
381 am33xx_clkdm_deny_idle(clkdm);
382 break;
383 case OMAP34XX_CLKSTCTRL_FORCE_SLEEP:
384 am33xx_clkdm_sleep(clkdm);
385 break;
386 case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP:
387 am33xx_clkdm_wakeup(clkdm);
388 break;
389 case OMAP34XX_CLKSTCTRL_ENABLE_AUTO:
390 am33xx_clkdm_allow_idle(clkdm);
391 break;
392 }
393 return 0;
394 }
395
396 struct clkdm_ops am33xx_clkdm_operations = {
397 .clkdm_sleep = am33xx_clkdm_sleep,
398 .clkdm_wakeup = am33xx_clkdm_wakeup,
399 .clkdm_allow_idle = am33xx_clkdm_allow_idle,
400 .clkdm_deny_idle = am33xx_clkdm_deny_idle,
401 .clkdm_clk_enable = am33xx_clkdm_clk_enable,
402 .clkdm_clk_disable = am33xx_clkdm_clk_disable,
403 .clkdm_save_context = am33xx_clkdm_save_context,
404 .clkdm_restore_context = am33xx_clkdm_restore_context,
405 };
406
407 static const struct cm_ll_data am33xx_cm_ll_data = {
408 .wait_module_ready = &am33xx_cm_wait_module_ready,
409 .wait_module_idle = &am33xx_cm_wait_module_idle,
410 .module_enable = &am33xx_cm_module_enable,
411 .module_disable = &am33xx_cm_module_disable,
412 .xlate_clkctrl = &am33xx_cm_xlate_clkctrl,
413 };
414
am33xx_cm_init(const struct omap_prcm_init_data * data)415 int __init am33xx_cm_init(const struct omap_prcm_init_data *data)
416 {
417 return cm_register(&am33xx_cm_ll_data);
418 }
419
am33xx_cm_exit(void)420 static void __exit am33xx_cm_exit(void)
421 {
422 cm_unregister(&am33xx_cm_ll_data);
423 }
424 __exitcall(am33xx_cm_exit);
425