cm33xx.c (11925B)
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 */ 51static 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 */ 57static 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 */ 63static 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 75static 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 */ 94static 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 */ 110static 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 */ 129static 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 */ 149static 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 */ 168static 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 */ 182static 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 */ 195static 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 */ 208static 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 */ 229static 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 */ 252static 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 */ 273static 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 */ 292static 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 305static 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 311static 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 317static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm) 318{ 319 am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); 320} 321 322static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm) 323{ 324 am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); 325} 326 327static 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 335static 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 347static 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 */ 358static 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 */ 373static 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 392struct 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 403static 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 411int __init am33xx_cm_init(const struct omap_prcm_init_data *data) 412{ 413 return cm_register(&am33xx_cm_ll_data); 414} 415 416static void __exit am33xx_cm_exit(void) 417{ 418 cm_unregister(&am33xx_cm_ll_data); 419} 420__exitcall(am33xx_cm_exit);