linux/arch/arm/mach-omap2/cm2xxx.c
<<
>>
Prefs
   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);
 400