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 */ 43 static 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 */ 49 static 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 */ 55 static 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 67 static 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 */ 86 static 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 */ 102 static 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 */ 121 static 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 */ 141 static 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 */ 160 static 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 */ 174 static 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 */ 187 static 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 */ 200 static 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 */ 221 static 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 */ 244 static 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 */ 265 static 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 */ 284 static 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 297 static 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 303 static 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 309 static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm) 310 { 311 am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); 312 } 313 314 static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm) 315 { 316 am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); 317 } 318 319 static 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 327 static 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 339 static 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 */ 350 static 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 */ 365 static 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 384 struct 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 395 static 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 403 int __init am33xx_cm_init(const struct omap_prcm_init_data *data) 404 { 405 return cm_register(&am33xx_cm_ll_data); 406 } 407 408 static void __exit am33xx_cm_exit(void) 409 { 410 cm_unregister(&am33xx_cm_ll_data); 411 } 412 __exitcall(am33xx_cm_exit); 413
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.