Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * OMAP2xxx CM module functions
0004  *
0005  * Copyright (C) 2009 Nokia Corporation
0006  * Copyright (C) 2008-2010, 2012 Texas Instruments, Inc.
0007  * Paul Walmsley
0008  * Rajendra Nayak <rnayak@ti.com>
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 /* CM_AUTOIDLE_PLL.AUTO_* bit values for DPLLs */
0025 #define DPLL_AUTOIDLE_DISABLE               0x0
0026 #define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP       0x3
0027 
0028 /* CM_AUTOIDLE_PLL.AUTO_* bit values for APLLs (OMAP2xxx only) */
0029 #define OMAP2XXX_APLL_AUTOIDLE_DISABLE          0x0
0030 #define OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP       0x3
0031 
0032 /* CM_IDLEST_PLL bit value offset for APLLs (OMAP2xxx only) */
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  * DPLL autoidle control
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  * APLL control
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 /* Enable an APLL if off */
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;   /* apll already enabled */
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      * REVISIT: Should we return an error code if
0154      * omap2xxx_cm_wait_module_ready() fails?
0155      */
0156     return 0;
0157 }
0158 
0159 /* Stop APLL */
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 /* Enable an APLL if off */
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 /* Enable an APLL if off */
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 /* Stop APLL */
0184 void omap2xxx_cm_apll54_disable(void)
0185 {
0186     _omap2xxx_apll_disable(OMAP24XX_EN_54M_PLL_SHIFT);
0187 }
0188 
0189 /* Stop APLL */
0190 void omap2xxx_cm_apll96_disable(void)
0191 {
0192     _omap2xxx_apll_disable(OMAP24XX_EN_96M_PLL_SHIFT);
0193 }
0194 
0195 /**
0196  * omap2xxx_cm_split_idlest_reg - split CM_IDLEST reg addr into its components
0197  * @idlest_reg: CM_IDLEST* virtual address
0198  * @prcm_inst: pointer to an s16 to return the PRCM instance offset
0199  * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID
0200  *
0201  * XXX This function is only needed until absolute register addresses are
0202  * removed from the OMAP struct clk records.
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  * omap2xxx_cm_wait_module_ready - wait for a module to leave idle or standby
0236  * @part: PRCM partition, ignored for OMAP2
0237  * @prcm_mod: PRCM module offset
0238  * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3)
0239  * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
0240  *
0241  * Wait for the PRCM to indicate that the module identified by
0242  * (@prcm_mod, @idlest_id, @idlest_shift) is clocked.  Return 0 upon
0243  * success or -EBUSY if the module doesn't enable in time.
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 /* Clockdomain low-level functions */
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     /* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */
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     /* Check for UART3. */
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);