1/* 2 * AM33XX CM functions 3 * 4 * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://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 + 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 + 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 75/** 76 * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield 77 * @inst: CM instance register offset (*_INST macro) 78 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 79 * 80 * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to 81 * bit 0. 82 */ 83static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs) 84{ 85 u32 v = am33xx_cm_read_reg(inst, clkctrl_offs); 86 v &= AM33XX_IDLEST_MASK; 87 v >>= AM33XX_IDLEST_SHIFT; 88 return v; 89} 90 91/** 92 * _is_module_ready - can module registers be accessed without causing an abort? 93 * @inst: CM instance register offset (*_INST macro) 94 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 95 * 96 * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either 97 * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise. 98 */ 99static bool _is_module_ready(u16 inst, u16 clkctrl_offs) 100{ 101 u32 v; 102 103 v = _clkctrl_idlest(inst, clkctrl_offs); 104 105 return (v == CLKCTRL_IDLEST_FUNCTIONAL || 106 v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false; 107} 108 109/** 110 * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield 111 * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted) 112 * @inst: CM instance register offset (*_INST macro) 113 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 114 * 115 * @c must be the unshifted value for CLKTRCTRL - i.e., this function 116 * will handle the shift itself. 117 */ 118static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs) 119{ 120 u32 v; 121 122 v = am33xx_cm_read_reg(inst, cdoffs); 123 v &= ~AM33XX_CLKTRCTRL_MASK; 124 v |= c << AM33XX_CLKTRCTRL_SHIFT; 125 am33xx_cm_write_reg(v, inst, cdoffs); 126} 127 128/* Public functions */ 129 130/** 131 * am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode? 132 * @inst: CM instance register offset (*_INST macro) 133 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 134 * 135 * Returns true if the clockdomain referred to by (@inst, @cdoffs) 136 * is in hardware-supervised idle mode, or 0 otherwise. 137 */ 138static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs) 139{ 140 u32 v; 141 142 v = am33xx_cm_read_reg(inst, cdoffs); 143 v &= AM33XX_CLKTRCTRL_MASK; 144 v >>= AM33XX_CLKTRCTRL_SHIFT; 145 146 return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false; 147} 148 149/** 150 * am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode 151 * @inst: CM instance register offset (*_INST macro) 152 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 153 * 154 * Put a clockdomain referred to by (@inst, @cdoffs) into 155 * hardware-supervised idle mode. No return value. 156 */ 157static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs) 158{ 159 _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs); 160} 161 162/** 163 * am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode 164 * @inst: CM instance register offset (*_INST macro) 165 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 166 * 167 * Put a clockdomain referred to by (@inst, @cdoffs) into 168 * software-supervised idle mode, i.e., controlled manually by the 169 * Linux OMAP clockdomain code. No return value. 170 */ 171static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs) 172{ 173 _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs); 174} 175 176/** 177 * am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle 178 * @inst: CM instance register offset (*_INST macro) 179 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 180 * 181 * Put a clockdomain referred to by (@inst, @cdoffs) into idle 182 * No return value. 183 */ 184static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs) 185{ 186 _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs); 187} 188 189/** 190 * am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle 191 * @inst: CM instance register offset (*_INST macro) 192 * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 193 * 194 * Take a clockdomain referred to by (@inst, @cdoffs) out of idle, 195 * waking it up. No return value. 196 */ 197static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs) 198{ 199 _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs); 200} 201 202/* 203 * 204 */ 205 206/** 207 * am33xx_cm_wait_module_ready - wait for a module to be in 'func' state 208 * @part: PRCM partition, ignored for AM33xx 209 * @inst: CM instance register offset (*_INST macro) 210 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 211 * @bit_shift: bit shift for the register, ignored for AM33xx 212 * 213 * Wait for the module IDLEST to be functional. If the idle state is in any 214 * the non functional state (trans, idle or disabled), module and thus the 215 * sysconfig cannot be accessed and will probably lead to an "imprecise 216 * external abort" 217 */ 218static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs, 219 u8 bit_shift) 220{ 221 int i = 0; 222 223 omap_test_timeout(_is_module_ready(inst, clkctrl_offs), 224 MAX_MODULE_READY_TIME, i); 225 226 return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; 227} 228 229/** 230 * am33xx_cm_wait_module_idle - wait for a module to be in 'disabled' 231 * state 232 * @part: CM partition, ignored for AM33xx 233 * @inst: CM instance register offset (*_INST macro) 234 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 235 * @bit_shift: bit shift for the register, ignored for AM33xx 236 * 237 * Wait for the module IDLEST to be disabled. Some PRCM transition, 238 * like reset assertion or parent clock de-activation must wait the 239 * module to be fully disabled. 240 */ 241static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs, 242 u8 bit_shift) 243{ 244 int i = 0; 245 246 if (!clkctrl_offs) 247 return 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 339struct clkdm_ops am33xx_clkdm_operations = { 340 .clkdm_sleep = am33xx_clkdm_sleep, 341 .clkdm_wakeup = am33xx_clkdm_wakeup, 342 .clkdm_allow_idle = am33xx_clkdm_allow_idle, 343 .clkdm_deny_idle = am33xx_clkdm_deny_idle, 344 .clkdm_clk_enable = am33xx_clkdm_clk_enable, 345 .clkdm_clk_disable = am33xx_clkdm_clk_disable, 346}; 347 348static struct cm_ll_data am33xx_cm_ll_data = { 349 .wait_module_ready = &am33xx_cm_wait_module_ready, 350 .wait_module_idle = &am33xx_cm_wait_module_idle, 351 .module_enable = &am33xx_cm_module_enable, 352 .module_disable = &am33xx_cm_module_disable, 353}; 354 355int __init am33xx_cm_init(const struct omap_prcm_init_data *data) 356{ 357 return cm_register(&am33xx_cm_ll_data); 358} 359 360static void __exit am33xx_cm_exit(void) 361{ 362 cm_unregister(&am33xx_cm_ll_data); 363} 364__exitcall(am33xx_cm_exit); 365