linux/arch/arm/mach-omap2/cm33xx.c
<<
>>
Prefs
   1/*
   2 * AM33XX CM functions
   3 *
   4 * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://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.va + 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.va + 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
  75static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask)
  76{
  77        u32 v;
  78
  79        v = am33xx_cm_read_reg(inst, idx);
  80        v &= mask;
  81        v >>= __ffs(mask);
  82
  83        return v;
  84}
  85
  86/**
  87 * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
  88 * @inst: CM instance register offset (*_INST macro)
  89 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
  90 *
  91 * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
  92 * bit 0.
  93 */
  94static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs)
  95{
  96        u32 v = am33xx_cm_read_reg(inst, clkctrl_offs);
  97        v &= AM33XX_IDLEST_MASK;
  98        v >>= AM33XX_IDLEST_SHIFT;
  99        return v;
 100}
 101
 102/**
 103 * _is_module_ready - can module registers be accessed without causing an abort?
 104 * @inst: CM instance register offset (*_INST macro)
 105 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 106 *
 107 * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
 108 * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
 109 */
 110static bool _is_module_ready(u16 inst, u16 clkctrl_offs)
 111{
 112        u32 v;
 113
 114        v = _clkctrl_idlest(inst, clkctrl_offs);
 115
 116        return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
 117                v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
 118}
 119
 120/**
 121 * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield
 122 * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted)
 123 * @inst: CM instance register offset (*_INST macro)
 124 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 125 *
 126 * @c must be the unshifted value for CLKTRCTRL - i.e., this function
 127 * will handle the shift itself.
 128 */
 129static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs)
 130{
 131        u32 v;
 132
 133        v = am33xx_cm_read_reg(inst, cdoffs);
 134        v &= ~AM33XX_CLKTRCTRL_MASK;
 135        v |= c << AM33XX_CLKTRCTRL_SHIFT;
 136        am33xx_cm_write_reg(v, inst, cdoffs);
 137}
 138
 139/* Public functions */
 140
 141/**
 142 * am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode?
 143 * @inst: CM instance register offset (*_INST macro)
 144 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 145 *
 146 * Returns true if the clockdomain referred to by (@inst, @cdoffs)
 147 * is in hardware-supervised idle mode, or 0 otherwise.
 148 */
 149static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs)
 150{
 151        u32 v;
 152
 153        v = am33xx_cm_read_reg(inst, cdoffs);
 154        v &= AM33XX_CLKTRCTRL_MASK;
 155        v >>= AM33XX_CLKTRCTRL_SHIFT;
 156
 157        return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false;
 158}
 159
 160/**
 161 * am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode
 162 * @inst: CM instance register offset (*_INST macro)
 163 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 164 *
 165 * Put a clockdomain referred to by (@inst, @cdoffs) into
 166 * hardware-supervised idle mode.  No return value.
 167 */
 168static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs)
 169{
 170        _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs);
 171}
 172
 173/**
 174 * am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode
 175 * @inst: CM instance register offset (*_INST macro)
 176 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 177 *
 178 * Put a clockdomain referred to by (@inst, @cdoffs) into
 179 * software-supervised idle mode, i.e., controlled manually by the
 180 * Linux OMAP clockdomain code.  No return value.
 181 */
 182static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs)
 183{
 184        _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs);
 185}
 186
 187/**
 188 * am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle
 189 * @inst: CM instance register offset (*_INST macro)
 190 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 191 *
 192 * Put a clockdomain referred to by (@inst, @cdoffs) into idle
 193 * No return value.
 194 */
 195static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs)
 196{
 197        _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs);
 198}
 199
 200/**
 201 * am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle
 202 * @inst: CM instance register offset (*_INST macro)
 203 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 204 *
 205 * Take a clockdomain referred to by (@inst, @cdoffs) out of idle,
 206 * waking it up.  No return value.
 207 */
 208static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs)
 209{
 210        _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs);
 211}
 212
 213/*
 214 *
 215 */
 216
 217/**
 218 * am33xx_cm_wait_module_ready - wait for a module to be in 'func' state
 219 * @part: PRCM partition, ignored for AM33xx
 220 * @inst: CM instance register offset (*_INST macro)
 221 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 222 * @bit_shift: bit shift for the register, ignored for AM33xx
 223 *
 224 * Wait for the module IDLEST to be functional. If the idle state is in any
 225 * the non functional state (trans, idle or disabled), module and thus the
 226 * sysconfig cannot be accessed and will probably lead to an "imprecise
 227 * external abort"
 228 */
 229static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs,
 230                                       u8 bit_shift)
 231{
 232        int i = 0;
 233
 234        omap_test_timeout(_is_module_ready(inst, clkctrl_offs),
 235                          MAX_MODULE_READY_TIME, i);
 236
 237        return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
 238}
 239
 240/**
 241 * am33xx_cm_wait_module_idle - wait for a module to be in 'disabled'
 242 * state
 243 * @part: CM partition, ignored for AM33xx
 244 * @inst: CM instance register offset (*_INST macro)
 245 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 246 * @bit_shift: bit shift for the register, ignored for AM33xx
 247 *
 248 * Wait for the module IDLEST to be disabled. Some PRCM transition,
 249 * like reset assertion or parent clock de-activation must wait the
 250 * module to be fully disabled.
 251 */
 252static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs,
 253                                      u8 bit_shift)
 254{
 255        int i = 0;
 256
 257        omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) ==
 258                                CLKCTRL_IDLEST_DISABLED),
 259                                MAX_MODULE_READY_TIME, i);
 260
 261        return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
 262}
 263
 264/**
 265 * am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL
 266 * @mode: Module mode (SW or HW)
 267 * @part: CM partition, ignored for AM33xx
 268 * @inst: CM instance register offset (*_INST macro)
 269 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 270 *
 271 * No return value.
 272 */
 273static void am33xx_cm_module_enable(u8 mode, u8 part, u16 inst,
 274                                    u16 clkctrl_offs)
 275{
 276        u32 v;
 277
 278        v = am33xx_cm_read_reg(inst, clkctrl_offs);
 279        v &= ~AM33XX_MODULEMODE_MASK;
 280        v |= mode << AM33XX_MODULEMODE_SHIFT;
 281        am33xx_cm_write_reg(v, inst, clkctrl_offs);
 282}
 283
 284/**
 285 * am33xx_cm_module_disable - Disable the module inside CLKCTRL
 286 * @part: CM partition, ignored for AM33xx
 287 * @inst: CM instance register offset (*_INST macro)
 288 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 289 *
 290 * No return value.
 291 */
 292static void am33xx_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs)
 293{
 294        u32 v;
 295
 296        v = am33xx_cm_read_reg(inst, clkctrl_offs);
 297        v &= ~AM33XX_MODULEMODE_MASK;
 298        am33xx_cm_write_reg(v, inst, clkctrl_offs);
 299}
 300
 301/*
 302 * Clockdomain low-level functions
 303 */
 304
 305static int am33xx_clkdm_sleep(struct clockdomain *clkdm)
 306{
 307        am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs);
 308        return 0;
 309}
 310
 311static int am33xx_clkdm_wakeup(struct clockdomain *clkdm)
 312{
 313        am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs);
 314        return 0;
 315}
 316
 317static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
 318{
 319        am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
 320}
 321
 322static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
 323{
 324        am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
 325}
 326
 327static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm)
 328{
 329        if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
 330                return am33xx_clkdm_wakeup(clkdm);
 331
 332        return 0;
 333}
 334
 335static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm)
 336{
 337        bool hwsup = false;
 338
 339        hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
 340
 341        if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
 342                am33xx_clkdm_sleep(clkdm);
 343
 344        return 0;
 345}
 346
 347static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset)
 348{
 349        return cm_base.pa + inst + offset;
 350}
 351
 352/**
 353 * am33xx_clkdm_save_context - Save the clockdomain transition context
 354 * @clkdm: The clockdomain pointer whose context needs to be saved
 355 *
 356 * Save the clockdomain transition context.
 357 */
 358static int am33xx_clkdm_save_context(struct clockdomain *clkdm)
 359{
 360        clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst,
 361                                                 clkdm->clkdm_offs,
 362                                                 AM33XX_CLKTRCTRL_MASK);
 363
 364        return 0;
 365}
 366
 367/**
 368 * am33xx_restore_save_context - Restore the clockdomain transition context
 369 * @clkdm: The clockdomain pointer whose context needs to be restored
 370 *
 371 * Restore the clockdomain transition context.
 372 */
 373static int am33xx_clkdm_restore_context(struct clockdomain *clkdm)
 374{
 375        switch (clkdm->context) {
 376        case OMAP34XX_CLKSTCTRL_DISABLE_AUTO:
 377                am33xx_clkdm_deny_idle(clkdm);
 378                break;
 379        case OMAP34XX_CLKSTCTRL_FORCE_SLEEP:
 380                am33xx_clkdm_sleep(clkdm);
 381                break;
 382        case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP:
 383                am33xx_clkdm_wakeup(clkdm);
 384                break;
 385        case OMAP34XX_CLKSTCTRL_ENABLE_AUTO:
 386                am33xx_clkdm_allow_idle(clkdm);
 387                break;
 388        }
 389        return 0;
 390}
 391
 392struct clkdm_ops am33xx_clkdm_operations = {
 393        .clkdm_sleep            = am33xx_clkdm_sleep,
 394        .clkdm_wakeup           = am33xx_clkdm_wakeup,
 395        .clkdm_allow_idle       = am33xx_clkdm_allow_idle,
 396        .clkdm_deny_idle        = am33xx_clkdm_deny_idle,
 397        .clkdm_clk_enable       = am33xx_clkdm_clk_enable,
 398        .clkdm_clk_disable      = am33xx_clkdm_clk_disable,
 399        .clkdm_save_context     = am33xx_clkdm_save_context,
 400        .clkdm_restore_context  = am33xx_clkdm_restore_context,
 401};
 402
 403static const struct cm_ll_data am33xx_cm_ll_data = {
 404        .wait_module_ready      = &am33xx_cm_wait_module_ready,
 405        .wait_module_idle       = &am33xx_cm_wait_module_idle,
 406        .module_enable          = &am33xx_cm_module_enable,
 407        .module_disable         = &am33xx_cm_module_disable,
 408        .xlate_clkctrl          = &am33xx_cm_xlate_clkctrl,
 409};
 410
 411int __init am33xx_cm_init(const struct omap_prcm_init_data *data)
 412{
 413        return cm_register(&am33xx_cm_ll_data);
 414}
 415
 416static void __exit am33xx_cm_exit(void)
 417{
 418        cm_unregister(&am33xx_cm_ll_data);
 419}
 420__exitcall(am33xx_cm_exit);
 421