Skip to content

Commit c0db2ea

Browse files
Juergen Beisertrschwebel
authored andcommitted
MXC family: Add clock handling
Internal clock path handling for the mxc CPUs. Changed against the original Freescale code (and against clocklib for example): - clock rate is always calculated whenever one ask for the current rate (means struct clk has no more a member called "rate"). So switching the PLL base frequency will propagate immediately to all other clocks that are depending on this frequency. Signed-off-by: Juergen Beisert <[email protected]>
1 parent 38a41fd commit c0db2ea

File tree

3 files changed

+399
-1
lines changed

3 files changed

+399
-1
lines changed

arch/arm/plat-mxc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
#
44

55
# Common support
6-
obj-y := irq.o
6+
obj-y := irq.o clock.o

arch/arm/plat-mxc/clock.c

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/*
2+
* Based on arch/arm/plat-omap/clock.c
3+
*
4+
* Copyright (C) 2004 - 2005 Nokia corporation
5+
* Written by Tuukka Tikkanen <[email protected]>
6+
* Modified for omap shared clock framework by Tony Lindgren <[email protected]>
7+
* Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
8+
* Copyright 2008 Juergen Beisert, [email protected]
9+
*
10+
* This program is free software; you can redistribute it and/or
11+
* modify it under the terms of the GNU General Public License
12+
* as published by the Free Software Foundation; either version 2
13+
* of the License, or (at your option) any later version.
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program; if not, write to the Free Software
21+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22+
* MA 02110-1301, USA.
23+
*/
24+
25+
/* #define DEBUG */
26+
27+
#include <linux/clk.h>
28+
#include <linux/err.h>
29+
#include <linux/errno.h>
30+
#include <linux/init.h>
31+
#include <linux/io.h>
32+
#include <linux/kernel.h>
33+
#include <linux/list.h>
34+
#include <linux/module.h>
35+
#include <linux/mutex.h>
36+
#include <linux/platform_device.h>
37+
#include <linux/proc_fs.h>
38+
#include <linux/semaphore.h>
39+
#include <linux/string.h>
40+
#include <linux/version.h>
41+
42+
#include <asm/arch/clock.h>
43+
44+
static LIST_HEAD(clocks);
45+
static DEFINE_MUTEX(clocks_mutex);
46+
47+
/*-------------------------------------------------------------------------
48+
* Standard clock functions defined in include/linux/clk.h
49+
*-------------------------------------------------------------------------*/
50+
51+
/*
52+
* Retrieve a clock by name.
53+
*
54+
* Note that we first try to use device id on the bus
55+
* and clock name. If this fails, we try to use "<name>.<id>". If this fails,
56+
* we try to use clock name only.
57+
* The reference count to the clock's module owner ref count is incremented.
58+
*/
59+
struct clk *clk_get(struct device *dev, const char *id)
60+
{
61+
struct clk *p, *clk = ERR_PTR(-ENOENT);
62+
int idno;
63+
const char *str;
64+
65+
if (id == NULL)
66+
return clk;
67+
68+
if (dev == NULL || dev->bus != &platform_bus_type)
69+
idno = -1;
70+
else
71+
idno = to_platform_device(dev)->id;
72+
73+
mutex_lock(&clocks_mutex);
74+
75+
list_for_each_entry(p, &clocks, node) {
76+
if (p->id == idno &&
77+
strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
78+
clk = p;
79+
goto found;
80+
}
81+
}
82+
83+
str = strrchr(id, '.');
84+
if (str) {
85+
int cnt = str - id;
86+
str++;
87+
idno = simple_strtol(str, NULL, 10);
88+
list_for_each_entry(p, &clocks, node) {
89+
if (p->id == idno &&
90+
strlen(p->name) == cnt &&
91+
strncmp(id, p->name, cnt) == 0 &&
92+
try_module_get(p->owner)) {
93+
clk = p;
94+
goto found;
95+
}
96+
}
97+
}
98+
99+
list_for_each_entry(p, &clocks, node) {
100+
if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
101+
clk = p;
102+
goto found;
103+
}
104+
}
105+
106+
printk(KERN_WARNING "clk: Unable to get requested clock: %s\n", id);
107+
108+
found:
109+
mutex_unlock(&clocks_mutex);
110+
111+
return clk;
112+
}
113+
EXPORT_SYMBOL(clk_get);
114+
115+
static void __clk_disable(struct clk *clk)
116+
{
117+
if (clk == NULL || IS_ERR(clk))
118+
return;
119+
120+
__clk_disable(clk->parent);
121+
__clk_disable(clk->secondary);
122+
123+
if (!(--clk->usecount) && clk->disable)
124+
clk->disable(clk);
125+
}
126+
127+
static int __clk_enable(struct clk *clk)
128+
{
129+
if (clk == NULL || IS_ERR(clk))
130+
return -EINVAL;
131+
132+
__clk_enable(clk->parent);
133+
__clk_enable(clk->secondary);
134+
135+
if (clk->usecount++ == 0 && clk->enable)
136+
clk->enable(clk);
137+
138+
return 0;
139+
}
140+
141+
/* This function increments the reference count on the clock and enables the
142+
* clock if not already enabled. The parent clock tree is recursively enabled
143+
*/
144+
int clk_enable(struct clk *clk)
145+
{
146+
int ret = 0;
147+
148+
if (clk == NULL || IS_ERR(clk))
149+
return -EINVAL;
150+
151+
mutex_lock(&clocks_mutex);
152+
ret = __clk_enable(clk);
153+
mutex_unlock(&clocks_mutex);
154+
155+
return ret;
156+
}
157+
EXPORT_SYMBOL(clk_enable);
158+
159+
/* This function decrements the reference count on the clock and disables
160+
* the clock when reference count is 0. The parent clock tree is
161+
* recursively disabled
162+
*/
163+
void clk_disable(struct clk *clk)
164+
{
165+
if (clk == NULL || IS_ERR(clk))
166+
return;
167+
168+
mutex_lock(&clocks_mutex);
169+
__clk_disable(clk);
170+
mutex_unlock(&clocks_mutex);
171+
}
172+
EXPORT_SYMBOL(clk_disable);
173+
174+
/* Retrieve the *current* clock rate. If the clock itself
175+
* does not provide a special calculation routine, ask
176+
* its parent and so on, until one is able to return
177+
* a valid clock rate
178+
*/
179+
unsigned long clk_get_rate(struct clk *clk)
180+
{
181+
if (clk == NULL || IS_ERR(clk))
182+
return 0UL;
183+
184+
if (clk->get_rate)
185+
return clk->get_rate(clk);
186+
187+
return clk_get_rate(clk->parent);
188+
}
189+
EXPORT_SYMBOL(clk_get_rate);
190+
191+
/* Decrement the clock's module reference count */
192+
void clk_put(struct clk *clk)
193+
{
194+
if (clk && !IS_ERR(clk))
195+
module_put(clk->owner);
196+
}
197+
EXPORT_SYMBOL(clk_put);
198+
199+
/* Round the requested clock rate to the nearest supported
200+
* rate that is less than or equal to the requested rate.
201+
* This is dependent on the clock's current parent.
202+
*/
203+
long clk_round_rate(struct clk *clk, unsigned long rate)
204+
{
205+
if (clk == NULL || IS_ERR(clk) || !clk->round_rate)
206+
return 0;
207+
208+
return clk->round_rate(clk, rate);
209+
}
210+
EXPORT_SYMBOL(clk_round_rate);
211+
212+
/* Set the clock to the requested clock rate. The rate must
213+
* match a supported rate exactly based on what clk_round_rate returns
214+
*/
215+
int clk_set_rate(struct clk *clk, unsigned long rate)
216+
{
217+
int ret = -EINVAL;
218+
219+
if (clk == NULL || IS_ERR(clk) || clk->set_rate == NULL || rate == 0)
220+
return ret;
221+
222+
mutex_lock(&clocks_mutex);
223+
ret = clk->set_rate(clk, rate);
224+
mutex_unlock(&clocks_mutex);
225+
226+
return ret;
227+
}
228+
EXPORT_SYMBOL(clk_set_rate);
229+
230+
/* Set the clock's parent to another clock source */
231+
int clk_set_parent(struct clk *clk, struct clk *parent)
232+
{
233+
int ret = -EINVAL;
234+
235+
if (clk == NULL || IS_ERR(clk) || parent == NULL ||
236+
IS_ERR(parent) || clk->set_parent == NULL)
237+
return ret;
238+
239+
mutex_lock(&clocks_mutex);
240+
ret = clk->set_parent(clk, parent);
241+
if (ret == 0)
242+
clk->parent = parent;
243+
mutex_unlock(&clocks_mutex);
244+
245+
return ret;
246+
}
247+
EXPORT_SYMBOL(clk_set_parent);
248+
249+
/* Retrieve the clock's parent clock source */
250+
struct clk *clk_get_parent(struct clk *clk)
251+
{
252+
struct clk *ret = NULL;
253+
254+
if (clk == NULL || IS_ERR(clk))
255+
return ret;
256+
257+
return clk->parent;
258+
}
259+
EXPORT_SYMBOL(clk_get_parent);
260+
261+
/*
262+
* Add a new clock to the clock tree.
263+
*/
264+
int clk_register(struct clk *clk)
265+
{
266+
if (clk == NULL || IS_ERR(clk))
267+
return -EINVAL;
268+
269+
mutex_lock(&clocks_mutex);
270+
list_add(&clk->node, &clocks);
271+
mutex_unlock(&clocks_mutex);
272+
273+
return 0;
274+
}
275+
EXPORT_SYMBOL(clk_register);
276+
277+
/* Remove a clock from the clock tree */
278+
void clk_unregister(struct clk *clk)
279+
{
280+
if (clk == NULL || IS_ERR(clk))
281+
return;
282+
283+
mutex_lock(&clocks_mutex);
284+
list_del(&clk->node);
285+
mutex_unlock(&clocks_mutex);
286+
}
287+
EXPORT_SYMBOL(clk_unregister);
288+
289+
#ifdef CONFIG_PROC_FS
290+
static int mxc_clock_read_proc(char *page, char **start, off_t off,
291+
int count, int *eof, void *data)
292+
{
293+
struct clk *clkp;
294+
char *p = page;
295+
int len;
296+
297+
list_for_each_entry(clkp, &clocks, node) {
298+
p += sprintf(p, "%s-%d:\t\t%lu, %d", clkp->name, clkp->id,
299+
clk_get_rate(clkp), clkp->usecount);
300+
if (clkp->parent)
301+
p += sprintf(p, ", %s-%d\n", clkp->parent->name,
302+
clkp->parent->id);
303+
else
304+
p += sprintf(p, "\n");
305+
}
306+
307+
len = (p - page) - off;
308+
if (len < 0)
309+
len = 0;
310+
311+
*eof = (len <= count) ? 1 : 0;
312+
*start = page + off;
313+
314+
return len;
315+
}
316+
317+
static int __init mxc_setup_proc_entry(void)
318+
{
319+
struct proc_dir_entry *res;
320+
321+
res = create_proc_read_entry("cpu/clocks", 0, NULL,
322+
mxc_clock_read_proc, NULL);
323+
if (!res) {
324+
printk(KERN_ERR "Failed to create proc/cpu/clocks\n");
325+
return -ENOMEM;
326+
}
327+
return 0;
328+
}
329+
330+
late_initcall(mxc_setup_proc_entry);
331+
#endif

0 commit comments

Comments
 (0)