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