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