Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
Note: File does not exist in v5.14.15.
  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 <linux/clkdev.h>
 25#include <mach/csp/hw_cfg.h>
 26#include <mach/csp/chipcHw_def.h>
 27#include <mach/csp/chipcHw_reg.h>
 28#include <mach/csp/chipcHw_inline.h>
 29
 30#include "clock.h"
 31
 32#define clk_is_primary(x)       ((x)->type & CLK_TYPE_PRIMARY)
 33#define clk_is_pll1(x)          ((x)->type & CLK_TYPE_PLL1)
 34#define clk_is_pll2(x)          ((x)->type & CLK_TYPE_PLL2)
 35#define clk_is_programmable(x)  ((x)->type & CLK_TYPE_PROGRAMMABLE)
 36#define clk_is_bypassable(x)    ((x)->type & CLK_TYPE_BYPASSABLE)
 37
 38#define clk_is_using_xtal(x)    ((x)->mode & CLK_MODE_XTAL)
 39
 40static DEFINE_SPINLOCK(clk_lock);
 41
 42static void __clk_enable(struct clk *clk)
 43{
 44	if (!clk)
 45		return;
 46
 47	/* enable parent clock first */
 48	if (clk->parent)
 49		__clk_enable(clk->parent);
 50
 51	if (clk->use_cnt++ == 0) {
 52		if (clk_is_pll1(clk)) {	/* PLL1 */
 53			chipcHw_pll1Enable(clk->rate_hz, 0);
 54		} else if (clk_is_pll2(clk)) {	/* PLL2 */
 55			chipcHw_pll2Enable(clk->rate_hz);
 56		} else if (clk_is_using_xtal(clk)) {	/* source is crystal */
 57			if (!clk_is_primary(clk))
 58				chipcHw_bypassClockEnable(clk->csp_id);
 59		} else {	/* source is PLL */
 60			chipcHw_setClockEnable(clk->csp_id);
 61		}
 62	}
 63}
 64
 65int clk_enable(struct clk *clk)
 66{
 67	unsigned long flags;
 68
 69	if (!clk)
 70		return -EINVAL;
 71
 72	spin_lock_irqsave(&clk_lock, flags);
 73	__clk_enable(clk);
 74	spin_unlock_irqrestore(&clk_lock, flags);
 75
 76	return 0;
 77}
 78EXPORT_SYMBOL(clk_enable);
 79
 80static void __clk_disable(struct clk *clk)
 81{
 82	if (!clk)
 83		return;
 84
 85	BUG_ON(clk->use_cnt == 0);
 86
 87	if (--clk->use_cnt == 0) {
 88		if (clk_is_pll1(clk)) {	/* PLL1 */
 89			chipcHw_pll1Disable();
 90		} else if (clk_is_pll2(clk)) {	/* PLL2 */
 91			chipcHw_pll2Disable();
 92		} else if (clk_is_using_xtal(clk)) {	/* source is crystal */
 93			if (!clk_is_primary(clk))
 94				chipcHw_bypassClockDisable(clk->csp_id);
 95		} else {	/* source is PLL */
 96			chipcHw_setClockDisable(clk->csp_id);
 97		}
 98	}
 99
100	if (clk->parent)
101		__clk_disable(clk->parent);
102}
103
104void clk_disable(struct clk *clk)
105{
106	unsigned long flags;
107
108	if (!clk)
109		return;
110
111	spin_lock_irqsave(&clk_lock, flags);
112	__clk_disable(clk);
113	spin_unlock_irqrestore(&clk_lock, flags);
114}
115EXPORT_SYMBOL(clk_disable);
116
117unsigned long clk_get_rate(struct clk *clk)
118{
119	if (!clk)
120		return 0;
121
122	return clk->rate_hz;
123}
124EXPORT_SYMBOL(clk_get_rate);
125
126long clk_round_rate(struct clk *clk, unsigned long rate)
127{
128	unsigned long flags;
129	unsigned long actual;
130	unsigned long rate_hz;
131
132	if (!clk)
133		return -EINVAL;
134
135	if (!clk_is_programmable(clk))
136		return -EINVAL;
137
138	if (clk->use_cnt)
139		return -EBUSY;
140
141	spin_lock_irqsave(&clk_lock, flags);
142	actual = clk->parent->rate_hz;
143	rate_hz = min(actual, rate);
144	spin_unlock_irqrestore(&clk_lock, flags);
145
146	return rate_hz;
147}
148EXPORT_SYMBOL(clk_round_rate);
149
150int clk_set_rate(struct clk *clk, unsigned long rate)
151{
152	unsigned long flags;
153	unsigned long actual;
154	unsigned long rate_hz;
155
156	if (!clk)
157		return -EINVAL;
158
159	if (!clk_is_programmable(clk))
160		return -EINVAL;
161
162	if (clk->use_cnt)
163		return -EBUSY;
164
165	spin_lock_irqsave(&clk_lock, flags);
166	actual = clk->parent->rate_hz;
167	rate_hz = min(actual, rate);
168	rate_hz = chipcHw_setClockFrequency(clk->csp_id, rate_hz);
169	clk->rate_hz = rate_hz;
170	spin_unlock_irqrestore(&clk_lock, flags);
171
172	return 0;
173}
174EXPORT_SYMBOL(clk_set_rate);
175
176struct clk *clk_get_parent(struct clk *clk)
177{
178	if (!clk)
179		return NULL;
180
181	return clk->parent;
182}
183EXPORT_SYMBOL(clk_get_parent);
184
185int clk_set_parent(struct clk *clk, struct clk *parent)
186{
187	unsigned long flags;
188	struct clk *old_parent;
189
190	if (!clk || !parent)
191		return -EINVAL;
192
193	if (!clk_is_primary(parent) || !clk_is_bypassable(clk))
194		return -EINVAL;
195
196	/* if more than one user, parent is not allowed */
197	if (clk->use_cnt > 1)
198		return -EBUSY;
199
200	if (clk->parent == parent)
201		return 0;
202
203	spin_lock_irqsave(&clk_lock, flags);
204	old_parent = clk->parent;
205	clk->parent = parent;
206	if (clk_is_using_xtal(parent))
207		clk->mode |= CLK_MODE_XTAL;
208	else
209		clk->mode &= (~CLK_MODE_XTAL);
210
211	/* if clock is active */
212	if (clk->use_cnt != 0) {
213		clk->use_cnt--;
214		/* enable clock with the new parent */
215		__clk_enable(clk);
216		/* disable the old parent */
217		__clk_disable(old_parent);
218	}
219	spin_unlock_irqrestore(&clk_lock, flags);
220
221	return 0;
222}
223EXPORT_SYMBOL(clk_set_parent);