root/drivers/clk/ti/clkt_dflt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _wait_idlest_generic
  2. _omap2_module_wait_ready
  3. omap2_clk_dflt_find_companion
  4. omap2_clk_dflt_find_idlest
  5. omap2_dflt_clk_enable
  6. omap2_dflt_clk_disable
  7. omap2_dflt_clk_is_enabled

   1 /*
   2  * Default clock type
   3  *
   4  * Copyright (C) 2005-2008, 2015 Texas Instruments, Inc.
   5  * Copyright (C) 2004-2010 Nokia Corporation
   6  *
   7  * Contacts:
   8  * Richard Woodruff <r-woodruff2@ti.com>
   9  * Paul Walmsley
  10  * Tero Kristo <t-kristo@ti.com>
  11  *
  12  * This program is free software; you can redistribute it and/or modify
  13  * it under the terms of the GNU General Public License version 2 as
  14  * published by the Free Software Foundation.
  15  *
  16  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  17  * kind, whether express or implied; without even the implied warranty
  18  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19  * GNU General Public License for more details.
  20  */
  21 
  22 #include <linux/kernel.h>
  23 #include <linux/errno.h>
  24 #include <linux/clk-provider.h>
  25 #include <linux/io.h>
  26 #include <linux/clk/ti.h>
  27 #include <linux/delay.h>
  28 
  29 #include "clock.h"
  30 
  31 /*
  32  * MAX_MODULE_ENABLE_WAIT: maximum of number of microseconds to wait
  33  * for a module to indicate that it is no longer in idle
  34  */
  35 #define MAX_MODULE_ENABLE_WAIT          100000
  36 
  37 /*
  38  * CM module register offsets, used for calculating the companion
  39  * register addresses.
  40  */
  41 #define CM_FCLKEN                       0x0000
  42 #define CM_ICLKEN                       0x0010
  43 
  44 /**
  45  * _wait_idlest_generic - wait for a module to leave the idle state
  46  * @clk: module clock to wait for (needed for register offsets)
  47  * @reg: virtual address of module IDLEST register
  48  * @mask: value to mask against to determine if the module is active
  49  * @idlest: idle state indicator (0 or 1) for the clock
  50  * @name: name of the clock (for printk)
  51  *
  52  * Wait for a module to leave idle, where its idle-status register is
  53  * not inside the CM module.  Returns 1 if the module left idle
  54  * promptly, or 0 if the module did not leave idle before the timeout
  55  * elapsed.  XXX Deprecated - should be moved into drivers for the
  56  * individual IP block that the IDLEST register exists in.
  57  */
  58 static int _wait_idlest_generic(struct clk_hw_omap *clk,
  59                                 struct clk_omap_reg *reg,
  60                                 u32 mask, u8 idlest, const char *name)
  61 {
  62         int i = 0, ena = 0;
  63 
  64         ena = (idlest) ? 0 : mask;
  65 
  66         /* Wait until module enters enabled state */
  67         for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
  68                 if ((ti_clk_ll_ops->clk_readl(reg) & mask) == ena)
  69                         break;
  70                 udelay(1);
  71         }
  72 
  73         if (i < MAX_MODULE_ENABLE_WAIT)
  74                 pr_debug("omap clock: module associated with clock %s ready after %d loops\n",
  75                          name, i);
  76         else
  77                 pr_err("omap clock: module associated with clock %s didn't enable in %d tries\n",
  78                        name, MAX_MODULE_ENABLE_WAIT);
  79 
  80         return (i < MAX_MODULE_ENABLE_WAIT) ? 1 : 0;
  81 }
  82 
  83 /**
  84  * _omap2_module_wait_ready - wait for an OMAP module to leave IDLE
  85  * @clk: struct clk * belonging to the module
  86  *
  87  * If the necessary clocks for the OMAP hardware IP block that
  88  * corresponds to clock @clk are enabled, then wait for the module to
  89  * indicate readiness (i.e., to leave IDLE).  This code does not
  90  * belong in the clock code and will be moved in the medium term to
  91  * module-dependent code.  No return value.
  92  */
  93 static void _omap2_module_wait_ready(struct clk_hw_omap *clk)
  94 {
  95         struct clk_omap_reg companion_reg, idlest_reg;
  96         u8 other_bit, idlest_bit, idlest_val, idlest_reg_id;
  97         s16 prcm_mod;
  98         int r;
  99 
 100         /* Not all modules have multiple clocks that their IDLEST depends on */
 101         if (clk->ops->find_companion) {
 102                 clk->ops->find_companion(clk, &companion_reg, &other_bit);
 103                 if (!(ti_clk_ll_ops->clk_readl(&companion_reg) &
 104                       (1 << other_bit)))
 105                         return;
 106         }
 107 
 108         clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit, &idlest_val);
 109         r = ti_clk_ll_ops->cm_split_idlest_reg(&idlest_reg, &prcm_mod,
 110                                                &idlest_reg_id);
 111         if (r) {
 112                 /* IDLEST register not in the CM module */
 113                 _wait_idlest_generic(clk, &idlest_reg, (1 << idlest_bit),
 114                                      idlest_val, clk_hw_get_name(&clk->hw));
 115         } else {
 116                 ti_clk_ll_ops->cm_wait_module_ready(0, prcm_mod, idlest_reg_id,
 117                                                     idlest_bit);
 118         }
 119 }
 120 
 121 /**
 122  * omap2_clk_dflt_find_companion - find companion clock to @clk
 123  * @clk: struct clk * to find the companion clock of
 124  * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
 125  * @other_bit: u8 ** to return the companion clock bit shift in
 126  *
 127  * Note: We don't need special code here for INVERT_ENABLE for the
 128  * time being since INVERT_ENABLE only applies to clocks enabled by
 129  * CM_CLKEN_PLL
 130  *
 131  * Convert CM_ICLKEN* <-> CM_FCLKEN*.  This conversion assumes it's
 132  * just a matter of XORing the bits.
 133  *
 134  * Some clocks don't have companion clocks.  For example, modules with
 135  * only an interface clock (such as MAILBOXES) don't have a companion
 136  * clock.  Right now, this code relies on the hardware exporting a bit
 137  * in the correct companion register that indicates that the
 138  * nonexistent 'companion clock' is active.  Future patches will
 139  * associate this type of code with per-module data structures to
 140  * avoid this issue, and remove the casts.  No return value.
 141  */
 142 void omap2_clk_dflt_find_companion(struct clk_hw_omap *clk,
 143                                    struct clk_omap_reg *other_reg,
 144                                    u8 *other_bit)
 145 {
 146         memcpy(other_reg, &clk->enable_reg, sizeof(*other_reg));
 147 
 148         /*
 149          * Convert CM_ICLKEN* <-> CM_FCLKEN*.  This conversion assumes
 150          * it's just a matter of XORing the bits.
 151          */
 152         other_reg->offset ^= (CM_FCLKEN ^ CM_ICLKEN);
 153 
 154         *other_bit = clk->enable_bit;
 155 }
 156 
 157 /**
 158  * omap2_clk_dflt_find_idlest - find CM_IDLEST reg va, bit shift for @clk
 159  * @clk: struct clk * to find IDLEST info for
 160  * @idlest_reg: void __iomem ** to return the CM_IDLEST va in
 161  * @idlest_bit: u8 * to return the CM_IDLEST bit shift in
 162  * @idlest_val: u8 * to return the idle status indicator
 163  *
 164  * Return the CM_IDLEST register address and bit shift corresponding
 165  * to the module that "owns" this clock.  This default code assumes
 166  * that the CM_IDLEST bit shift is the CM_*CLKEN bit shift, and that
 167  * the IDLEST register address ID corresponds to the CM_*CLKEN
 168  * register address ID (e.g., that CM_FCLKEN2 corresponds to
 169  * CM_IDLEST2).  This is not true for all modules.  No return value.
 170  */
 171 void omap2_clk_dflt_find_idlest(struct clk_hw_omap *clk,
 172                                 struct clk_omap_reg *idlest_reg, u8 *idlest_bit,
 173                                 u8 *idlest_val)
 174 {
 175         memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg));
 176 
 177         idlest_reg->offset &= ~0xf0;
 178         idlest_reg->offset |= 0x20;
 179 
 180         *idlest_bit = clk->enable_bit;
 181 
 182         /*
 183          * 24xx uses 0 to indicate not ready, and 1 to indicate ready.
 184          * 34xx reverses this, just to keep us on our toes
 185          * AM35xx uses both, depending on the module.
 186          */
 187         *idlest_val = ti_clk_get_features()->cm_idlest_val;
 188 }
 189 
 190 /**
 191  * omap2_dflt_clk_enable - enable a clock in the hardware
 192  * @hw: struct clk_hw * of the clock to enable
 193  *
 194  * Enable the clock @hw in the hardware.  We first call into the OMAP
 195  * clockdomain code to "enable" the corresponding clockdomain if this
 196  * is the first enabled user of the clockdomain.  Then program the
 197  * hardware to enable the clock.  Then wait for the IP block that uses
 198  * this clock to leave idle (if applicable).  Returns the error value
 199  * from clkdm_clk_enable() if it terminated with an error, or -EINVAL
 200  * if @hw has a null clock enable_reg, or zero upon success.
 201  */
 202 int omap2_dflt_clk_enable(struct clk_hw *hw)
 203 {
 204         struct clk_hw_omap *clk;
 205         u32 v;
 206         int ret = 0;
 207         bool clkdm_control;
 208 
 209         if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL)
 210                 clkdm_control = false;
 211         else
 212                 clkdm_control = true;
 213 
 214         clk = to_clk_hw_omap(hw);
 215 
 216         if (clkdm_control && clk->clkdm) {
 217                 ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk);
 218                 if (ret) {
 219                         WARN(1,
 220                              "%s: could not enable %s's clockdomain %s: %d\n",
 221                              __func__, clk_hw_get_name(hw),
 222                              clk->clkdm_name, ret);
 223                         return ret;
 224                 }
 225         }
 226 
 227         /* FIXME should not have INVERT_ENABLE bit here */
 228         v = ti_clk_ll_ops->clk_readl(&clk->enable_reg);
 229         if (clk->flags & INVERT_ENABLE)
 230                 v &= ~(1 << clk->enable_bit);
 231         else
 232                 v |= (1 << clk->enable_bit);
 233         ti_clk_ll_ops->clk_writel(v, &clk->enable_reg);
 234         v = ti_clk_ll_ops->clk_readl(&clk->enable_reg); /* OCP barrier */
 235 
 236         if (clk->ops && clk->ops->find_idlest)
 237                 _omap2_module_wait_ready(clk);
 238 
 239         return 0;
 240 }
 241 
 242 /**
 243  * omap2_dflt_clk_disable - disable a clock in the hardware
 244  * @hw: struct clk_hw * of the clock to disable
 245  *
 246  * Disable the clock @hw in the hardware, and call into the OMAP
 247  * clockdomain code to "disable" the corresponding clockdomain if all
 248  * clocks/hwmods in that clockdomain are now disabled.  No return
 249  * value.
 250  */
 251 void omap2_dflt_clk_disable(struct clk_hw *hw)
 252 {
 253         struct clk_hw_omap *clk;
 254         u32 v;
 255 
 256         clk = to_clk_hw_omap(hw);
 257 
 258         v = ti_clk_ll_ops->clk_readl(&clk->enable_reg);
 259         if (clk->flags & INVERT_ENABLE)
 260                 v |= (1 << clk->enable_bit);
 261         else
 262                 v &= ~(1 << clk->enable_bit);
 263         ti_clk_ll_ops->clk_writel(v, &clk->enable_reg);
 264         /* No OCP barrier needed here since it is a disable operation */
 265 
 266         if (!(ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) &&
 267             clk->clkdm)
 268                 ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk);
 269 }
 270 
 271 /**
 272  * omap2_dflt_clk_is_enabled - is clock enabled in the hardware?
 273  * @hw: struct clk_hw * to check
 274  *
 275  * Return 1 if the clock represented by @hw is enabled in the
 276  * hardware, or 0 otherwise.  Intended for use in the struct
 277  * clk_ops.is_enabled function pointer.
 278  */
 279 int omap2_dflt_clk_is_enabled(struct clk_hw *hw)
 280 {
 281         struct clk_hw_omap *clk = to_clk_hw_omap(hw);
 282         u32 v;
 283 
 284         v = ti_clk_ll_ops->clk_readl(&clk->enable_reg);
 285 
 286         if (clk->flags & INVERT_ENABLE)
 287                 v ^= BIT(clk->enable_bit);
 288 
 289         v &= BIT(clk->enable_bit);
 290 
 291         return v ? 1 : 0;
 292 }
 293 
 294 const struct clk_hw_omap_ops clkhwops_wait = {
 295         .find_idlest    = omap2_clk_dflt_find_idlest,
 296         .find_companion = omap2_clk_dflt_find_companion,
 297 };

/* [<][>][^][v][top][bottom][index][help] */