cm2xxx.c (9558B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * OMAP2xxx CM module functions 4 * 5 * Copyright (C) 2009 Nokia Corporation 6 * Copyright (C) 2008-2010, 2012 Texas Instruments, Inc. 7 * Paul Walmsley 8 * Rajendra Nayak <rnayak@ti.com> 9 */ 10 11#include <linux/kernel.h> 12#include <linux/types.h> 13#include <linux/delay.h> 14#include <linux/errno.h> 15#include <linux/err.h> 16#include <linux/io.h> 17 18#include "prm2xxx.h" 19#include "cm.h" 20#include "cm2xxx.h" 21#include "cm-regbits-24xx.h" 22#include "clockdomain.h" 23 24/* CM_AUTOIDLE_PLL.AUTO_* bit values for DPLLs */ 25#define DPLL_AUTOIDLE_DISABLE 0x0 26#define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP 0x3 27 28/* CM_AUTOIDLE_PLL.AUTO_* bit values for APLLs (OMAP2xxx only) */ 29#define OMAP2XXX_APLL_AUTOIDLE_DISABLE 0x0 30#define OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP 0x3 31 32/* CM_IDLEST_PLL bit value offset for APLLs (OMAP2xxx only) */ 33#define EN_APLL_LOCKED 3 34 35static const u8 omap2xxx_cm_idlest_offs[] = { 36 CM_IDLEST1, CM_IDLEST2, OMAP2430_CM_IDLEST3, OMAP24XX_CM_IDLEST4 37}; 38 39/* 40 * 41 */ 42 43static void _write_clktrctrl(u8 c, s16 module, u32 mask) 44{ 45 u32 v; 46 47 v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); 48 v &= ~mask; 49 v |= c << __ffs(mask); 50 omap2_cm_write_mod_reg(v, module, OMAP2_CM_CLKSTCTRL); 51} 52 53static bool omap2xxx_cm_is_clkdm_in_hwsup(s16 module, u32 mask) 54{ 55 u32 v; 56 57 v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); 58 v &= mask; 59 v >>= __ffs(mask); 60 61 return (v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; 62} 63 64static void omap2xxx_cm_clkdm_enable_hwsup(s16 module, u32 mask) 65{ 66 _write_clktrctrl(OMAP24XX_CLKSTCTRL_ENABLE_AUTO, module, mask); 67} 68 69static void omap2xxx_cm_clkdm_disable_hwsup(s16 module, u32 mask) 70{ 71 _write_clktrctrl(OMAP24XX_CLKSTCTRL_DISABLE_AUTO, module, mask); 72} 73 74/* 75 * DPLL autoidle control 76 */ 77 78static void _omap2xxx_set_dpll_autoidle(u8 m) 79{ 80 u32 v; 81 82 v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); 83 v &= ~OMAP24XX_AUTO_DPLL_MASK; 84 v |= m << OMAP24XX_AUTO_DPLL_SHIFT; 85 omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); 86} 87 88void omap2xxx_cm_set_dpll_disable_autoidle(void) 89{ 90 _omap2xxx_set_dpll_autoidle(OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP); 91} 92 93void omap2xxx_cm_set_dpll_auto_low_power_stop(void) 94{ 95 _omap2xxx_set_dpll_autoidle(DPLL_AUTOIDLE_DISABLE); 96} 97 98/* 99 * APLL control 100 */ 101 102static void _omap2xxx_set_apll_autoidle(u8 m, u32 mask) 103{ 104 u32 v; 105 106 v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); 107 v &= ~mask; 108 v |= m << __ffs(mask); 109 omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); 110} 111 112void omap2xxx_cm_set_apll54_disable_autoidle(void) 113{ 114 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP, 115 OMAP24XX_AUTO_54M_MASK); 116} 117 118void omap2xxx_cm_set_apll54_auto_low_power_stop(void) 119{ 120 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE, 121 OMAP24XX_AUTO_54M_MASK); 122} 123 124void omap2xxx_cm_set_apll96_disable_autoidle(void) 125{ 126 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP, 127 OMAP24XX_AUTO_96M_MASK); 128} 129 130void omap2xxx_cm_set_apll96_auto_low_power_stop(void) 131{ 132 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE, 133 OMAP24XX_AUTO_96M_MASK); 134} 135 136/* Enable an APLL if off */ 137static int _omap2xxx_apll_enable(u8 enable_bit, u8 status_bit) 138{ 139 u32 v, m; 140 141 m = EN_APLL_LOCKED << enable_bit; 142 143 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); 144 if (v & m) 145 return 0; /* apll already enabled */ 146 147 v |= m; 148 omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); 149 150 omap2xxx_cm_wait_module_ready(0, PLL_MOD, 1, status_bit); 151 152 /* 153 * REVISIT: Should we return an error code if 154 * omap2xxx_cm_wait_module_ready() fails? 155 */ 156 return 0; 157} 158 159/* Stop APLL */ 160static void _omap2xxx_apll_disable(u8 enable_bit) 161{ 162 u32 v; 163 164 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); 165 v &= ~(EN_APLL_LOCKED << enable_bit); 166 omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); 167} 168 169/* Enable an APLL if off */ 170int omap2xxx_cm_apll54_enable(void) 171{ 172 return _omap2xxx_apll_enable(OMAP24XX_EN_54M_PLL_SHIFT, 173 OMAP24XX_ST_54M_APLL_SHIFT); 174} 175 176/* Enable an APLL if off */ 177int omap2xxx_cm_apll96_enable(void) 178{ 179 return _omap2xxx_apll_enable(OMAP24XX_EN_96M_PLL_SHIFT, 180 OMAP24XX_ST_96M_APLL_SHIFT); 181} 182 183/* Stop APLL */ 184void omap2xxx_cm_apll54_disable(void) 185{ 186 _omap2xxx_apll_disable(OMAP24XX_EN_54M_PLL_SHIFT); 187} 188 189/* Stop APLL */ 190void omap2xxx_cm_apll96_disable(void) 191{ 192 _omap2xxx_apll_disable(OMAP24XX_EN_96M_PLL_SHIFT); 193} 194 195/** 196 * omap2xxx_cm_split_idlest_reg - split CM_IDLEST reg addr into its components 197 * @idlest_reg: CM_IDLEST* virtual address 198 * @prcm_inst: pointer to an s16 to return the PRCM instance offset 199 * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID 200 * 201 * XXX This function is only needed until absolute register addresses are 202 * removed from the OMAP struct clk records. 203 */ 204static int omap2xxx_cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, 205 s16 *prcm_inst, 206 u8 *idlest_reg_id) 207{ 208 unsigned long offs; 209 u8 idlest_offs; 210 int i; 211 212 idlest_offs = idlest_reg->offset & 0xff; 213 for (i = 0; i < ARRAY_SIZE(omap2xxx_cm_idlest_offs); i++) { 214 if (idlest_offs == omap2xxx_cm_idlest_offs[i]) { 215 *idlest_reg_id = i + 1; 216 break; 217 } 218 } 219 220 if (i == ARRAY_SIZE(omap2xxx_cm_idlest_offs)) 221 return -EINVAL; 222 223 offs = idlest_reg->offset; 224 offs &= 0xff00; 225 *prcm_inst = offs; 226 227 return 0; 228} 229 230/* 231 * 232 */ 233 234/** 235 * omap2xxx_cm_wait_module_ready - wait for a module to leave idle or standby 236 * @part: PRCM partition, ignored for OMAP2 237 * @prcm_mod: PRCM module offset 238 * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3) 239 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check 240 * 241 * Wait for the PRCM to indicate that the module identified by 242 * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon 243 * success or -EBUSY if the module doesn't enable in time. 244 */ 245int omap2xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id, 246 u8 idlest_shift) 247{ 248 int ena = 0, i = 0; 249 u8 cm_idlest_reg; 250 u32 mask; 251 252 if (!idlest_id || (idlest_id > ARRAY_SIZE(omap2xxx_cm_idlest_offs))) 253 return -EINVAL; 254 255 cm_idlest_reg = omap2xxx_cm_idlest_offs[idlest_id - 1]; 256 257 mask = 1 << idlest_shift; 258 ena = mask; 259 260 omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) & 261 mask) == ena), MAX_MODULE_READY_TIME, i); 262 263 return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; 264} 265 266/* Clockdomain low-level functions */ 267 268static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm) 269{ 270 omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, 271 clkdm->clktrctrl_mask); 272} 273 274static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm) 275{ 276 omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, 277 clkdm->clktrctrl_mask); 278} 279 280static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm) 281{ 282 bool hwsup = false; 283 284 if (!clkdm->clktrctrl_mask) 285 return 0; 286 287 hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, 288 clkdm->clktrctrl_mask); 289 if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) 290 omap2xxx_clkdm_wakeup(clkdm); 291 292 return 0; 293} 294 295static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm) 296{ 297 bool hwsup = false; 298 299 if (!clkdm->clktrctrl_mask) 300 return 0; 301 302 hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, 303 clkdm->clktrctrl_mask); 304 305 if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_SLEEP) 306 omap2xxx_clkdm_sleep(clkdm); 307 308 return 0; 309} 310 311struct clkdm_ops omap2_clkdm_operations = { 312 .clkdm_add_wkdep = omap2_clkdm_add_wkdep, 313 .clkdm_del_wkdep = omap2_clkdm_del_wkdep, 314 .clkdm_read_wkdep = omap2_clkdm_read_wkdep, 315 .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps, 316 .clkdm_sleep = omap2xxx_clkdm_sleep, 317 .clkdm_wakeup = omap2xxx_clkdm_wakeup, 318 .clkdm_allow_idle = omap2xxx_clkdm_allow_idle, 319 .clkdm_deny_idle = omap2xxx_clkdm_deny_idle, 320 .clkdm_clk_enable = omap2xxx_clkdm_clk_enable, 321 .clkdm_clk_disable = omap2xxx_clkdm_clk_disable, 322}; 323 324int omap2xxx_cm_fclks_active(void) 325{ 326 u32 f1, f2; 327 328 f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); 329 f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); 330 331 return (f1 | f2) ? 1 : 0; 332} 333 334int omap2xxx_cm_mpu_retention_allowed(void) 335{ 336 u32 l; 337 338 /* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */ 339 l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); 340 if (l & (OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK | 341 OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK | 342 OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK)) 343 return 0; 344 /* Check for UART3. */ 345 l = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); 346 if (l & OMAP24XX_EN_UART3_MASK) 347 return 0; 348 349 return 1; 350} 351 352u32 omap2xxx_cm_get_core_clk_src(void) 353{ 354 u32 v; 355 356 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); 357 v &= OMAP24XX_CORE_CLK_SRC_MASK; 358 359 return v; 360} 361 362u32 omap2xxx_cm_get_core_pll_config(void) 363{ 364 return omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); 365} 366 367void omap2xxx_cm_set_mod_dividers(u32 mpu, u32 dsp, u32 gfx, u32 core, u32 mdm) 368{ 369 u32 tmp; 370 371 omap2_cm_write_mod_reg(mpu, MPU_MOD, CM_CLKSEL); 372 omap2_cm_write_mod_reg(dsp, OMAP24XX_DSP_MOD, CM_CLKSEL); 373 omap2_cm_write_mod_reg(gfx, GFX_MOD, CM_CLKSEL); 374 tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & 375 OMAP24XX_CLKSEL_DSS2_MASK; 376 omap2_cm_write_mod_reg(core | tmp, CORE_MOD, CM_CLKSEL1); 377 if (mdm) 378 omap2_cm_write_mod_reg(mdm, OMAP2430_MDM_MOD, CM_CLKSEL); 379} 380 381/* 382 * 383 */ 384 385static const struct cm_ll_data omap2xxx_cm_ll_data = { 386 .split_idlest_reg = &omap2xxx_cm_split_idlest_reg, 387 .wait_module_ready = &omap2xxx_cm_wait_module_ready, 388}; 389 390int __init omap2xxx_cm_init(const struct omap_prcm_init_data *data) 391{ 392 return cm_register(&omap2xxx_cm_ll_data); 393} 394 395static void __exit omap2xxx_cm_exit(void) 396{ 397 cm_unregister(&omap2xxx_cm_ll_data); 398} 399__exitcall(omap2xxx_cm_exit);