0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/kernel.h>
0012 #include <linux/types.h>
0013 #include <linux/delay.h>
0014 #include <linux/errno.h>
0015 #include <linux/err.h>
0016 #include <linux/io.h>
0017
0018 #include "prm2xxx.h"
0019 #include "cm.h"
0020 #include "cm2xxx.h"
0021 #include "cm-regbits-24xx.h"
0022 #include "clockdomain.h"
0023
0024
0025 #define DPLL_AUTOIDLE_DISABLE 0x0
0026 #define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP 0x3
0027
0028
0029 #define OMAP2XXX_APLL_AUTOIDLE_DISABLE 0x0
0030 #define OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP 0x3
0031
0032
0033 #define EN_APLL_LOCKED 3
0034
0035 static const u8 omap2xxx_cm_idlest_offs[] = {
0036 CM_IDLEST1, CM_IDLEST2, OMAP2430_CM_IDLEST3, OMAP24XX_CM_IDLEST4
0037 };
0038
0039
0040
0041
0042
0043 static void _write_clktrctrl(u8 c, s16 module, u32 mask)
0044 {
0045 u32 v;
0046
0047 v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL);
0048 v &= ~mask;
0049 v |= c << __ffs(mask);
0050 omap2_cm_write_mod_reg(v, module, OMAP2_CM_CLKSTCTRL);
0051 }
0052
0053 static bool omap2xxx_cm_is_clkdm_in_hwsup(s16 module, u32 mask)
0054 {
0055 u32 v;
0056
0057 v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL);
0058 v &= mask;
0059 v >>= __ffs(mask);
0060
0061 return (v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0;
0062 }
0063
0064 static void omap2xxx_cm_clkdm_enable_hwsup(s16 module, u32 mask)
0065 {
0066 _write_clktrctrl(OMAP24XX_CLKSTCTRL_ENABLE_AUTO, module, mask);
0067 }
0068
0069 static void omap2xxx_cm_clkdm_disable_hwsup(s16 module, u32 mask)
0070 {
0071 _write_clktrctrl(OMAP24XX_CLKSTCTRL_DISABLE_AUTO, module, mask);
0072 }
0073
0074
0075
0076
0077
0078 static void _omap2xxx_set_dpll_autoidle(u8 m)
0079 {
0080 u32 v;
0081
0082 v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE);
0083 v &= ~OMAP24XX_AUTO_DPLL_MASK;
0084 v |= m << OMAP24XX_AUTO_DPLL_SHIFT;
0085 omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE);
0086 }
0087
0088 void omap2xxx_cm_set_dpll_disable_autoidle(void)
0089 {
0090 _omap2xxx_set_dpll_autoidle(OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP);
0091 }
0092
0093 void omap2xxx_cm_set_dpll_auto_low_power_stop(void)
0094 {
0095 _omap2xxx_set_dpll_autoidle(DPLL_AUTOIDLE_DISABLE);
0096 }
0097
0098
0099
0100
0101
0102 static void _omap2xxx_set_apll_autoidle(u8 m, u32 mask)
0103 {
0104 u32 v;
0105
0106 v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE);
0107 v &= ~mask;
0108 v |= m << __ffs(mask);
0109 omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE);
0110 }
0111
0112 void omap2xxx_cm_set_apll54_disable_autoidle(void)
0113 {
0114 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP,
0115 OMAP24XX_AUTO_54M_MASK);
0116 }
0117
0118 void omap2xxx_cm_set_apll54_auto_low_power_stop(void)
0119 {
0120 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE,
0121 OMAP24XX_AUTO_54M_MASK);
0122 }
0123
0124 void omap2xxx_cm_set_apll96_disable_autoidle(void)
0125 {
0126 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP,
0127 OMAP24XX_AUTO_96M_MASK);
0128 }
0129
0130 void omap2xxx_cm_set_apll96_auto_low_power_stop(void)
0131 {
0132 _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE,
0133 OMAP24XX_AUTO_96M_MASK);
0134 }
0135
0136
0137 static int _omap2xxx_apll_enable(u8 enable_bit, u8 status_bit)
0138 {
0139 u32 v, m;
0140
0141 m = EN_APLL_LOCKED << enable_bit;
0142
0143 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN);
0144 if (v & m)
0145 return 0;
0146
0147 v |= m;
0148 omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN);
0149
0150 omap2xxx_cm_wait_module_ready(0, PLL_MOD, 1, status_bit);
0151
0152
0153
0154
0155
0156 return 0;
0157 }
0158
0159
0160 static void _omap2xxx_apll_disable(u8 enable_bit)
0161 {
0162 u32 v;
0163
0164 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN);
0165 v &= ~(EN_APLL_LOCKED << enable_bit);
0166 omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN);
0167 }
0168
0169
0170 int omap2xxx_cm_apll54_enable(void)
0171 {
0172 return _omap2xxx_apll_enable(OMAP24XX_EN_54M_PLL_SHIFT,
0173 OMAP24XX_ST_54M_APLL_SHIFT);
0174 }
0175
0176
0177 int omap2xxx_cm_apll96_enable(void)
0178 {
0179 return _omap2xxx_apll_enable(OMAP24XX_EN_96M_PLL_SHIFT,
0180 OMAP24XX_ST_96M_APLL_SHIFT);
0181 }
0182
0183
0184 void omap2xxx_cm_apll54_disable(void)
0185 {
0186 _omap2xxx_apll_disable(OMAP24XX_EN_54M_PLL_SHIFT);
0187 }
0188
0189
0190 void omap2xxx_cm_apll96_disable(void)
0191 {
0192 _omap2xxx_apll_disable(OMAP24XX_EN_96M_PLL_SHIFT);
0193 }
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204 static int omap2xxx_cm_split_idlest_reg(struct clk_omap_reg *idlest_reg,
0205 s16 *prcm_inst,
0206 u8 *idlest_reg_id)
0207 {
0208 unsigned long offs;
0209 u8 idlest_offs;
0210 int i;
0211
0212 idlest_offs = idlest_reg->offset & 0xff;
0213 for (i = 0; i < ARRAY_SIZE(omap2xxx_cm_idlest_offs); i++) {
0214 if (idlest_offs == omap2xxx_cm_idlest_offs[i]) {
0215 *idlest_reg_id = i + 1;
0216 break;
0217 }
0218 }
0219
0220 if (i == ARRAY_SIZE(omap2xxx_cm_idlest_offs))
0221 return -EINVAL;
0222
0223 offs = idlest_reg->offset;
0224 offs &= 0xff00;
0225 *prcm_inst = offs;
0226
0227 return 0;
0228 }
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245 int omap2xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id,
0246 u8 idlest_shift)
0247 {
0248 int ena = 0, i = 0;
0249 u8 cm_idlest_reg;
0250 u32 mask;
0251
0252 if (!idlest_id || (idlest_id > ARRAY_SIZE(omap2xxx_cm_idlest_offs)))
0253 return -EINVAL;
0254
0255 cm_idlest_reg = omap2xxx_cm_idlest_offs[idlest_id - 1];
0256
0257 mask = 1 << idlest_shift;
0258 ena = mask;
0259
0260 omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) &
0261 mask) == ena), MAX_MODULE_READY_TIME, i);
0262
0263 return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
0264 }
0265
0266
0267
0268 static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm)
0269 {
0270 omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
0271 clkdm->clktrctrl_mask);
0272 }
0273
0274 static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm)
0275 {
0276 omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
0277 clkdm->clktrctrl_mask);
0278 }
0279
0280 static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm)
0281 {
0282 bool hwsup = false;
0283
0284 if (!clkdm->clktrctrl_mask)
0285 return 0;
0286
0287 hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
0288 clkdm->clktrctrl_mask);
0289 if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
0290 omap2xxx_clkdm_wakeup(clkdm);
0291
0292 return 0;
0293 }
0294
0295 static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm)
0296 {
0297 bool hwsup = false;
0298
0299 if (!clkdm->clktrctrl_mask)
0300 return 0;
0301
0302 hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
0303 clkdm->clktrctrl_mask);
0304
0305 if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
0306 omap2xxx_clkdm_sleep(clkdm);
0307
0308 return 0;
0309 }
0310
0311 struct clkdm_ops omap2_clkdm_operations = {
0312 .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
0313 .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
0314 .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
0315 .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
0316 .clkdm_sleep = omap2xxx_clkdm_sleep,
0317 .clkdm_wakeup = omap2xxx_clkdm_wakeup,
0318 .clkdm_allow_idle = omap2xxx_clkdm_allow_idle,
0319 .clkdm_deny_idle = omap2xxx_clkdm_deny_idle,
0320 .clkdm_clk_enable = omap2xxx_clkdm_clk_enable,
0321 .clkdm_clk_disable = omap2xxx_clkdm_clk_disable,
0322 };
0323
0324 int omap2xxx_cm_fclks_active(void)
0325 {
0326 u32 f1, f2;
0327
0328 f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
0329 f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2);
0330
0331 return (f1 | f2) ? 1 : 0;
0332 }
0333
0334 int omap2xxx_cm_mpu_retention_allowed(void)
0335 {
0336 u32 l;
0337
0338
0339 l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
0340 if (l & (OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK |
0341 OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK |
0342 OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK))
0343 return 0;
0344
0345 l = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2);
0346 if (l & OMAP24XX_EN_UART3_MASK)
0347 return 0;
0348
0349 return 1;
0350 }
0351
0352 u32 omap2xxx_cm_get_core_clk_src(void)
0353 {
0354 u32 v;
0355
0356 v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
0357 v &= OMAP24XX_CORE_CLK_SRC_MASK;
0358
0359 return v;
0360 }
0361
0362 u32 omap2xxx_cm_get_core_pll_config(void)
0363 {
0364 return omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
0365 }
0366
0367 void omap2xxx_cm_set_mod_dividers(u32 mpu, u32 dsp, u32 gfx, u32 core, u32 mdm)
0368 {
0369 u32 tmp;
0370
0371 omap2_cm_write_mod_reg(mpu, MPU_MOD, CM_CLKSEL);
0372 omap2_cm_write_mod_reg(dsp, OMAP24XX_DSP_MOD, CM_CLKSEL);
0373 omap2_cm_write_mod_reg(gfx, GFX_MOD, CM_CLKSEL);
0374 tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) &
0375 OMAP24XX_CLKSEL_DSS2_MASK;
0376 omap2_cm_write_mod_reg(core | tmp, CORE_MOD, CM_CLKSEL1);
0377 if (mdm)
0378 omap2_cm_write_mod_reg(mdm, OMAP2430_MDM_MOD, CM_CLKSEL);
0379 }
0380
0381
0382
0383
0384
0385 static const struct cm_ll_data omap2xxx_cm_ll_data = {
0386 .split_idlest_reg = &omap2xxx_cm_split_idlest_reg,
0387 .wait_module_ready = &omap2xxx_cm_wait_module_ready,
0388 };
0389
0390 int __init omap2xxx_cm_init(const struct omap_prcm_init_data *data)
0391 {
0392 return cm_register(&omap2xxx_cm_ll_data);
0393 }
0394
0395 static void __exit omap2xxx_cm_exit(void)
0396 {
0397 cm_unregister(&omap2xxx_cm_ll_data);
0398 }
0399 __exitcall(omap2xxx_cm_exit);