FILE
cm33xx.c
File preview with syntax highlighting.
linux-6.19.14/arch/arm/mach-omap2/cm33xx.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.