linux/arch/arm/mach-bcmring/clock.c
<<
>>
Prefs
   1/*****************************************************************************
   2* Copyright 2001 - 2009 Broadcom Corporation.  All rights reserved.
   3*
   4* Unless you and Broadcom execute a separate written software license
   5* agreement governing use of this software, this software is licensed to you
   6* under the terms of the GNU General Public License version 2, available at
   7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
   8*
   9* Notwithstanding the above, under no circumstances may you combine this
  10* software in any way with any other Broadcom software provided under a
  11* license other than the GPL, without Broadcom's express prior written
  12* consent.
  13*****************************************************************************/
  14
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/device.h>
  18#include <linux/list.h>
  19#include <linux/errno.h>
  20#include <linux/err.h>
  21#include <linux/string.h>
  22#include <linux/clk.h>
  23#include <linux/spinlock.h>
  24#include <mach/csp/hw_cfg.h>
  25#include <mach/csp/chipcHw_def.h>
  26#include <mach/csp/chipcHw_reg.h>
  27#include <mach/csp/chipcHw_inline.h>
  28
  29#include <asm/clkdev.h>
  30
  31#include "clock.h"
  32
  33#define clk_is_primary(x)       ((x)->type & CLK_TYPE_PRIMARY)
  34#define clk_is_pll1(x)          ((x)->type & CLK_TYPE_PLL1)
  35#define clk_is_pll2(x)          ((x)->type & CLK_TYPE_PLL2)
  36#define clk_is_programmable(x)  ((x)->type & CLK_TYPE_PROGRAMMABLE)
  37#define clk_is_bypassable(x)    ((x)->type & CLK_TYPE_BYPASSABLE)
  38
  39#define clk_is_using_xtal(x)    ((x)->mode & CLK_MODE_XTAL)
  40
  41static DEFINE_SPINLOCK(clk_lock);
  42
  43static void __clk_enable(struct clk *clk)
  44{
  45        if (!clk)
  46                return;
  47
  48        /* enable parent clock first */
  49        if (clk->parent)
  50                __clk_enable(clk->parent);
  51
  52        if (clk->use_cnt++ == 0) {
  53                if (clk_is_pll1(clk)) { /* PLL1 */
  54                        chipcHw_pll1Enable(clk->rate_hz, 0);
  55                } else if (clk_is_pll2(clk)) {  /* PLL2 */
  56                        chipcHw_pll2Enable(clk->rate_hz);
  57                } else if (clk_is_using_xtal(clk)) {    /* source is crystal */
  58                        if (!clk_is_primary(clk))
  59                                chipcHw_bypassClockEnable(clk->csp_id);
  60                } else {        /* source is PLL */
  61                        chipcHw_setClockEnable(clk->csp_id);
  62                }
  63        }
  64}
  65
  66int clk_enable(struct clk *clk)
  67{
  68        unsigned long flags;
  69
  70        if (!clk)
  71                return -EINVAL;
  72
  73        spin_lock_irqsave(&clk_lock, flags);
  74        __clk_enable(clk);
  75        spin_unlock_irqrestore(&clk_lock, flags);
  76
  77        return 0;
  78}
  79EXPORT_SYMBOL(clk_enable);
  80
  81static void __clk_disable(struct clk *clk)
  82{
  83        if (!clk)
  84                return;
  85
  86        BUG_ON(clk->use_cnt == 0);
  87
  88        if (--clk->use_cnt == 0) {
  89                if (clk_is_pll1(clk)) { /* PLL1 */
  90                        chipcHw_pll1Disable();
  91                } else if (clk_is_pll2(clk)) {  /* PLL2 */
  92                        chipcHw_pll2Disable();
  93                } else if (clk_is_using_xtal(clk)) {    /* source is crystal */
  94                        if (!clk_is_primary(clk))
  95                                chipcHw_bypassClockDisable(clk->csp_id);
  96                } else {        /* source is PLL */
  97                        chipcHw_setClockDisable(clk->csp_id);
  98                }
  99        }
 100
 101        if (clk->parent)
 102                __clk_disable(clk->parent);
 103}
 104
 105void clk_disable(struct clk *clk)
 106{
 107        unsigned long flags;
 108
 109        if (!clk)
 110                return;
 111
 112        spin_lock_irqsave(&clk_lock, flags);
 113        __clk_disable(clk);
 114        spin_unlock_irqrestore(&clk_lock, flags);
 115}
 116EXPORT_SYMBOL(clk_disable);
 117
 118unsigned long clk_get_rate(struct clk *clk)
 119{
 120        if (!clk)
 121                return 0;
 122
 123        return clk->rate_hz;
 124}
 125EXPORT_SYMBOL(clk_get_rate);
 126
 127long clk_round_rate(struct clk *clk, unsigned long rate)
 128{
 129        unsigned long flags;
 130        unsigned long actual;
 131        unsigned long rate_hz;
 132
 133        if (!clk)
 134                return -EINVAL;
 135
 136        if (!clk_is_programmable(clk))
 137                return -EINVAL;
 138
 139        if (clk->use_cnt)
 140                return -EBUSY;
 141
 142        spin_lock_irqsave(&clk_lock, flags);
 143        actual = clk->parent->rate_hz;
 144        rate_hz = min(actual, rate);
 145        spin_unlock_irqrestore(&clk_lock, flags);
 146
 147        return rate_hz;
 148}
 149EXPORT_SYMBOL(clk_round_rate);
 150
 151int clk_set_rate(struct clk *clk, unsigned long rate)
 152{
 153        unsigned long flags;
 154        unsigned long actual;
 155        unsigned long rate_hz;
 156
 157        if (!clk)
 158                return -EINVAL;
 159
 160        if (!clk_is_programmable(clk))
 161                return -EINVAL;
 162
 163        if (clk->use_cnt)
 164                return -EBUSY;
 165
 166        spin_lock_irqsave(&clk_lock, flags);
 167        actual = clk->parent->rate_hz;
 168        rate_hz = min(actual, rate);
 169        rate_hz = chipcHw_setClockFrequency(clk->csp_id, rate_hz);
 170        clk->rate_hz = rate_hz;
 171        spin_unlock_irqrestore(&clk_lock, flags);
 172
 173        return 0;
 174}
 175EXPORT_SYMBOL(clk_set_rate);
 176
 177struct clk *clk_get_parent(struct clk *clk)
 178{
 179        if (!clk)
 180                return NULL;
 181
 182        return clk->parent;
 183}
 184EXPORT_SYMBOL(clk_get_parent);
 185
 186int clk_set_parent(struct clk *clk, struct clk *parent)
 187{
 188        unsigned long flags;
 189        struct clk *old_parent;
 190
 191        if (!clk || !parent)
 192                return -EINVAL;
 193
 194        if (!clk_is_primary(parent) || !clk_is_bypassable(clk))
 195                return -EINVAL;
 196
 197        /* if more than one user, parent is not allowed */
 198        if (clk->use_cnt > 1)
 199                return -EBUSY;
 200
 201        if (clk->parent == parent)
 202                return 0;
 203
 204        spin_lock_irqsave(&clk_lock, flags);
 205        old_parent = clk->parent;
 206        clk->parent = parent;
 207        if (clk_is_using_xtal(parent))
 208                clk->mode |= CLK_MODE_XTAL;
 209        else
 210                clk->mode &= (~CLK_MODE_XTAL);
 211
 212        /* if clock is active */
 213        if (clk->use_cnt != 0) {
 214                clk->use_cnt--;
 215                /* enable clock with the new parent */
 216                __clk_enable(clk);
 217                /* disable the old parent */
 218                __clk_disable(old_parent);
 219        }
 220        spin_unlock_irqrestore(&clk_lock, flags);
 221
 222        return 0;
 223}
 224EXPORT_SYMBOL(clk_set_parent);
 225