File Index
Files and directories
Sign in
FILE

cm33xx.c

File preview with syntax highlighting.

linux-6.19.14/arch/arm/mach-omap2/cm33xx.c C
  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 */
 46static 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 */
 52static 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 */
 58static 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
 70static 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 */
 89static 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 */
105static 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 */
124static 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 */
144static 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 */
163static 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 */
177static 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 */
190static 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 */
203static 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 */
224static 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 */
247static 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 */
268static 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 */
287static 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
300static 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
306static 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
312static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
313{
314	am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
315}
316
317static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
318{
319	am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
320}
321
322static 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
330static 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
351static 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 */
362static 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 */
377static 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
396struct 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
407static 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
415int __init am33xx_cm_init(const struct omap_prcm_init_data *data)
416{
417	return cm_register(&am33xx_cm_ll_data);
418}
419
420static void __exit am33xx_cm_exit(void)
421{
422	cm_unregister(&am33xx_cm_ll_data);
423}
424__exitcall(am33xx_cm_exit);
~/ Read-only file preview.