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