// SPDX-License-Identifier: GPL-2.0-only
/*
* AM33XX CM functions
*
* Copyright ( C ) 2011 - 2012 Texas Instruments Incorporated - https : //www.ti.com/
* Vaibhav Hiremath < hvaibhav @ ti . com >
*
* Reference taken from OMAP4 cminst44xx . c
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include "clockdomain.h"
#include "cm.h"
#include "cm33xx.h"
#include "cm-regbits-34xx.h"
#include "cm-regbits-33xx.h"
#include "prm33xx.h"
#if IS_ENABLED(CONFIG_SUSPEND)
#include <linux/suspend.h>
#endif
/*
* CLKCTRL_IDLEST_ * : possible values for the CM_ * _ CLKCTRL . IDLEST bitfield :
*
* 0 x0 func : Module is fully functional , including OCP
* 0 x1 trans : Module is performing transition : wakeup , or sleep , or sleep
* abortion
* 0 x2 idle : Module is in Idle mode ( only OCP part ) . It is functional if
* using separate functional clock
* 0 x3 disabled : Module is disabled and cannot be accessed
*
*/
#define CLKCTRL_IDLEST_FUNCTIONAL 0 x0
#define CLKCTRL_IDLEST_INTRANSITION 0 x1
#define CLKCTRL_IDLEST_INTERFACE_IDLE 0 x2
#define CLKCTRL_IDLEST_DISABLED 0 x3
/* Private functions */
/* Read a register in a CM instance */
static inline u32 am33xx_cm_read_reg(u16 inst, u16 idx)
{
return readl_relaxed(cm_base.va + inst + idx);
}
/* Write into a register in a CM */
static inline void am33xx_cm_write_reg(u32 val, u16 inst, u16 idx)
{
writel_relaxed(val, cm_base.va + inst + idx);
}
/* Read-modify-write a register in CM */
static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx)
{
u32 v;
v = am33xx_cm_read_reg(inst, idx);
v &= ~mask;
v |= bits;
am33xx_cm_write_reg(v, inst, idx);
return v;
}
static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask)
{
u32 v;
v = am33xx_cm_read_reg(inst, idx);
v &= mask;
v >>= __ffs(mask);
return v;
}
/**
* _ clkctrl_idlest - read a CM_ * _ CLKCTRL register ; mask & shift IDLEST bitfield
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
*
* Return the IDLEST bitfield of a CM_ * _ CLKCTRL register , shifted down to
* bit 0 .
*/
static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs)
{
u32 v = am33xx_cm_read_reg(inst, clkctrl_offs);
v &= AM33XX_IDLEST_MASK;
v >>= AM33XX_IDLEST_SHIFT;
return v;
}
/**
* _ is_module_ready - can module registers be accessed without causing an abort ?
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
*
* Returns true if the module ' s CM_ * _ CLKCTRL . IDLEST bitfield is either
* * FUNCTIONAL or * INTERFACE_IDLE ; false otherwise .
*/
static bool _is_module_ready(u16 inst, u16 clkctrl_offs)
{
u32 v;
v = _clkctrl_idlest(inst, clkctrl_offs);
return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false ;
}
/**
* _ clktrctrl_write - write @ c to a CM_CLKSTCTRL . CLKTRCTRL register bitfield
* @ c : CLKTRCTRL register bitfield ( LSB = bit 0 , i . e . , unshifted )
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* @ c must be the unshifted value for CLKTRCTRL - i . e . , this function
* will handle the shift itself .
*/
static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs)
{
u32 v;
v = am33xx_cm_read_reg(inst, cdoffs);
v &= ~AM33XX_CLKTRCTRL_MASK;
v |= c << AM33XX_CLKTRCTRL_SHIFT;
am33xx_cm_write_reg(v, inst, cdoffs);
}
/* Public functions */
/**
* am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode ?
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* Returns true if the clockdomain referred to by ( @ inst , @ cdoffs )
* is in hardware - supervised idle mode , or 0 otherwise .
*/
static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs)
{
u32 v;
v = am33xx_cm_read_reg(inst, cdoffs);
v &= AM33XX_CLKTRCTRL_MASK;
v >>= AM33XX_CLKTRCTRL_SHIFT;
return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false ;
}
/**
* am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup - idle mode
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* Put a clockdomain referred to by ( @ inst , @ cdoffs ) into
* hardware - supervised idle mode . No return value .
*/
static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs)
{
_clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs);
}
/**
* am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup - idle mode
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* Put a clockdomain referred to by ( @ inst , @ cdoffs ) into
* software - supervised idle mode , i . e . , controlled manually by the
* Linux OMAP clockdomain code . No return value .
*/
static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs)
{
_clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs);
}
/**
* am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* Put a clockdomain referred to by ( @ inst , @ cdoffs ) into idle
* No return value .
*/
static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs)
{
_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs);
}
/**
* am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle
* @ inst : CM instance register offset ( * _ INST macro )
* @ cdoffs : Clockdomain register offset ( * _ CDOFFS macro )
*
* Take a clockdomain referred to by ( @ inst , @ cdoffs ) out of idle ,
* waking it up . No return value .
*/
static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs)
{
_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs);
}
/*
*
*/
/**
* am33xx_cm_wait_module_ready - wait for a module to be in ' func ' state
* @ part : PRCM partition , ignored for AM33xx
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
* @ bit_shift : bit shift for the register , ignored for AM33xx
*
* Wait for the module IDLEST to be functional . If the idle state is in any
* the non functional state ( trans , idle or disabled ) , module and thus the
* sysconfig cannot be accessed and will probably lead to an " imprecise
* external abort "
*/
static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs,
u8 bit_shift)
{
int i = 0 ;
omap_test_timeout(_is_module_ready(inst, clkctrl_offs),
MAX_MODULE_READY_TIME, i);
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
}
/**
* am33xx_cm_wait_module_idle - wait for a module to be in ' disabled '
* state
* @ part : CM partition , ignored for AM33xx
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
* @ bit_shift : bit shift for the register , ignored for AM33xx
*
* Wait for the module IDLEST to be disabled . Some PRCM transition ,
* like reset assertion or parent clock de - activation must wait the
* module to be fully disabled .
*/
static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs,
u8 bit_shift)
{
int i = 0 ;
omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) ==
CLKCTRL_IDLEST_DISABLED),
MAX_MODULE_READY_TIME, i);
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
}
/**
* am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL
* @ mode : Module mode ( SW or HW )
* @ part : CM partition , ignored for AM33xx
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
*
* No return value .
*/
static void am33xx_cm_module_enable(u8 mode, u8 part, u16 inst,
u16 clkctrl_offs)
{
u32 v;
v = am33xx_cm_read_reg(inst, clkctrl_offs);
v &= ~AM33XX_MODULEMODE_MASK;
v |= mode << AM33XX_MODULEMODE_SHIFT;
am33xx_cm_write_reg(v, inst, clkctrl_offs);
}
/**
* am33xx_cm_module_disable - Disable the module inside CLKCTRL
* @ part : CM partition , ignored for AM33xx
* @ inst : CM instance register offset ( * _ INST macro )
* @ clkctrl_offs : Module clock control register offset ( * _ CLKCTRL macro )
*
* No return value .
*/
static void am33xx_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs)
{
u32 v;
v = am33xx_cm_read_reg(inst, clkctrl_offs);
v &= ~AM33XX_MODULEMODE_MASK;
am33xx_cm_write_reg(v, inst, clkctrl_offs);
}
/*
* Clockdomain low - level functions
*/
static int am33xx_clkdm_sleep(struct clockdomain *clkdm)
{
am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs);
return 0 ;
}
static int am33xx_clkdm_wakeup(struct clockdomain *clkdm)
{
am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs);
return 0 ;
}
static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
{
am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
}
static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
{
am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
}
static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm)
{
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
return am33xx_clkdm_wakeup(clkdm);
return 0 ;
}
static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm)
{
bool hwsup = false ;
#if IS_ENABLED(CONFIG_SUSPEND)
/*
* In case of standby , Don ' t put the l4ls clk domain to sleep .
* Since CM3 PM FW doesn ' t wake - up / enable the l4ls clk domain
* upon wake - up , CM3 PM FW fails to wake - up th MPU .
*/
if (pm_suspend_target_state == PM_SUSPEND_STANDBY &&
(clkdm->flags & CLKDM_STANDBY_FORCE_WAKEUP))
return 0 ;
#endif
hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
am33xx_clkdm_sleep(clkdm);
return 0 ;
}
static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset)
{
return cm_base.pa + inst + offset;
}
/**
* am33xx_clkdm_save_context - Save the clockdomain transition context
* @ clkdm : The clockdomain pointer whose context needs to be saved
*
* Save the clockdomain transition context .
*/
static int am33xx_clkdm_save_context(struct clockdomain *clkdm)
{
clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst,
clkdm->clkdm_offs,
AM33XX_CLKTRCTRL_MASK);
return 0 ;
}
/**
* am33xx_clkdm_restore_context - Restore the clockdomain transition context
* @ clkdm : The clockdomain pointer whose context needs to be restored
*
* Restore the clockdomain transition context .
*/
static int am33xx_clkdm_restore_context(struct clockdomain *clkdm)
{
switch (clkdm->context) {
case OMAP34XX_CLKSTCTRL_DISABLE_AUTO:
am33xx_clkdm_deny_idle(clkdm);
break ;
case OMAP34XX_CLKSTCTRL_FORCE_SLEEP:
am33xx_clkdm_sleep(clkdm);
break ;
case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP:
am33xx_clkdm_wakeup(clkdm);
break ;
case OMAP34XX_CLKSTCTRL_ENABLE_AUTO:
am33xx_clkdm_allow_idle(clkdm);
break ;
}
return 0 ;
}
struct clkdm_ops am33xx_clkdm_operations = {
.clkdm_sleep = am33xx_clkdm_sleep,
.clkdm_wakeup = am33xx_clkdm_wakeup,
.clkdm_allow_idle = am33xx_clkdm_allow_idle,
.clkdm_deny_idle = am33xx_clkdm_deny_idle,
.clkdm_clk_enable = am33xx_clkdm_clk_enable,
.clkdm_clk_disable = am33xx_clkdm_clk_disable,
.clkdm_save_context = am33xx_clkdm_save_context,
.clkdm_restore_context = am33xx_clkdm_restore_context,
};
static const struct cm_ll_data am33xx_cm_ll_data = {
.wait_module_ready = &am33xx_cm_wait_module_ready,
.wait_module_idle = &am33xx_cm_wait_module_idle,
.module_enable = &am33xx_cm_module_enable,
.module_disable = &am33xx_cm_module_disable,
.xlate_clkctrl = &am33xx_cm_xlate_clkctrl,
};
int __init am33xx_cm_init(const struct omap_prcm_init_data *data)
{
return cm_register(&am33xx_cm_ll_data);
}
static void __exit am33xx_cm_exit(void )
{
cm_unregister(&am33xx_cm_ll_data);
}
__exitcall(am33xx_cm_exit);
Messung V0.5 in Prozent C=95 H=100 G=97
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland