aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/mxs/clk-frac.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2012-05-11 11:15:31 -0400
committerArnd Bergmann <arnd@arndb.de>2012-05-11 11:15:31 -0400
commit93c6d8927fa692faef3d7d945bd4fe84b0185ad4 (patch)
tree0a971c86114656ec4e39f7789d9faeb70432c36a /drivers/clk/mxs/clk-frac.c
parentea01d31a07ae182028d2398380948f5a4ee09953 (diff)
parent50260924afd4b745bfb6e5f1caee381a1875fc31 (diff)
Merge branch 'clk/mxs' of git://git.linaro.org/people/shawnguo/linux-2.6 into next/clock
Shawn Guo <shawn.guo@linaro.org> writes: mxs common clk porting for v3.5. It depends on the following two branches. [1] git://git.linaro.org/people/mturquette/linux.git clk-next [2] http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-arm.git clkdev As the mxs device tree conversion will constantly touch clock files, to save the conflicts, the updated mxs/dt branch coming later will based on this pull-request. * 'clk/mxs' of git://git.linaro.org/people/shawnguo/linux-2.6: ARM: mxs: remove now unused timer_clk argument from mxs_timer_init ARM: mxs: remove old clock support ARM: mxs: switch to common clk framework ARM: mxs: change the lookup name for fec phy clock ARM: mxs: request clock for timer clk: mxs: add clock support for imx28 clk: mxs: add clock support for imx23 clk: mxs: add mxs specific clocks Includes an update to Linux 3.4-rc6 Conflicts: drivers/clk/Makefile Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers/clk/mxs/clk-frac.c')
-rw-r--r--drivers/clk/mxs/clk-frac.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c
new file mode 100644
index 000000000000..e6aa6b567d68
--- /dev/null
+++ b/drivers/clk/mxs/clk-frac.c
@@ -0,0 +1,139 @@
1/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11
12#include <linux/clk.h>
13#include <linux/clk-provider.h>
14#include <linux/err.h>
15#include <linux/io.h>
16#include <linux/slab.h>
17#include "clk.h"
18
19/**
20 * struct clk_frac - mxs fractional divider clock
21 * @hw: clk_hw for the fractional divider clock
22 * @reg: register address
23 * @shift: the divider bit shift
24 * @width: the divider bit width
25 * @busy: busy bit shift
26 *
27 * The clock is an adjustable fractional divider with a busy bit to wait
28 * when the divider is adjusted.
29 */
30struct clk_frac {
31 struct clk_hw hw;
32 void __iomem *reg;
33 u8 shift;
34 u8 width;
35 u8 busy;
36};
37
38#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
39
40static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
41 unsigned long parent_rate)
42{
43 struct clk_frac *frac = to_clk_frac(hw);
44 u32 div;
45
46 div = readl_relaxed(frac->reg) >> frac->shift;
47 div &= (1 << frac->width) - 1;
48
49 return (parent_rate >> frac->width) * div;
50}
51
52static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
53 unsigned long *prate)
54{
55 struct clk_frac *frac = to_clk_frac(hw);
56 unsigned long parent_rate = *prate;
57 u32 div;
58 u64 tmp;
59
60 if (rate > parent_rate)
61 return -EINVAL;
62
63 tmp = rate;
64 tmp <<= frac->width;
65 do_div(tmp, parent_rate);
66 div = tmp;
67
68 if (!div)
69 return -EINVAL;
70
71 return (parent_rate >> frac->width) * div;
72}
73
74static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
75 unsigned long parent_rate)
76{
77 struct clk_frac *frac = to_clk_frac(hw);
78 unsigned long flags;
79 u32 div, val;
80 u64 tmp;
81
82 if (rate > parent_rate)
83 return -EINVAL;
84
85 tmp = rate;
86 tmp <<= frac->width;
87 do_div(tmp, parent_rate);
88 div = tmp;
89
90 if (!div)
91 return -EINVAL;
92
93 spin_lock_irqsave(&mxs_lock, flags);
94
95 val = readl_relaxed(frac->reg);
96 val &= ~(((1 << frac->width) - 1) << frac->shift);
97 val |= div << frac->shift;
98 writel_relaxed(val, frac->reg);
99
100 spin_unlock_irqrestore(&mxs_lock, flags);
101
102 return mxs_clk_wait(frac->reg, frac->busy);
103}
104
105static struct clk_ops clk_frac_ops = {
106 .recalc_rate = clk_frac_recalc_rate,
107 .round_rate = clk_frac_round_rate,
108 .set_rate = clk_frac_set_rate,
109};
110
111struct clk *mxs_clk_frac(const char *name, const char *parent_name,
112 void __iomem *reg, u8 shift, u8 width, u8 busy)
113{
114 struct clk_frac *frac;
115 struct clk *clk;
116 struct clk_init_data init;
117
118 frac = kzalloc(sizeof(*frac), GFP_KERNEL);
119 if (!frac)
120 return ERR_PTR(-ENOMEM);
121
122 init.name = name;
123 init.ops = &clk_frac_ops;
124 init.flags = CLK_SET_RATE_PARENT;
125 init.parent_names = (parent_name ? &parent_name: NULL);
126 init.num_parents = (parent_name ? 1 : 0);
127
128 frac->reg = reg;
129 frac->shift = shift;
130 frac->width = width;
131 frac->busy = busy;
132 frac->hw.init = &init;
133
134 clk = clk_register(NULL, &frac->hw);
135 if (IS_ERR(clk))
136 kfree(frac);
137
138 return clk;
139}