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