diff options
author | Liu Ying <Ying.Liu@freescale.com> | 2013-07-04 05:35:46 -0400 |
---|---|---|
committer | Shawn Guo <shawn.guo@linaro.org> | 2013-08-16 01:11:24 -0400 |
commit | a49e6c4b820488b79ada6da9ce609fa8611d3e00 (patch) | |
tree | ac05da5efae2363c81decfdbe941537fcf4d4996 /arch/arm/mach-imx/clk-fixup-mux.c | |
parent | cbe7fc8aaeefc71a75c2688602ba5bb570c0a265 (diff) |
ARM: imx: add common clock support for fixup mux
One register may have several fields to control some clocks. It
is possible that the read/write values of some fields may map to
different real functional values, so writing to the other fields
in the same register may break a working clock tree. A real case
is the aclk_podf field in the register 'CCM Serial Clock Multiplexer
Register 1' of i.MX6Q/SDL SoC. This patch introduces a fixup hook
for multiplexer clock which is called before writing a value to
clock registers to support this kind of multiplexer clocks.
Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Diffstat (limited to 'arch/arm/mach-imx/clk-fixup-mux.c')
-rw-r--r-- | arch/arm/mach-imx/clk-fixup-mux.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/clk-fixup-mux.c b/arch/arm/mach-imx/clk-fixup-mux.c new file mode 100644 index 000000000000..deb4b8093b30 --- /dev/null +++ b/arch/arm/mach-imx/clk-fixup-mux.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 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-provider.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include "clk.h" | ||
17 | |||
18 | #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) | ||
19 | |||
20 | /** | ||
21 | * struct clk_fixup_mux - imx integer fixup multiplexer clock | ||
22 | * @mux: the parent class | ||
23 | * @ops: pointer to clk_ops of parent class | ||
24 | * @fixup: a hook to fixup the write value | ||
25 | * | ||
26 | * The imx fixup multiplexer clock is a subclass of basic clk_mux | ||
27 | * with an addtional fixup hook. | ||
28 | */ | ||
29 | struct clk_fixup_mux { | ||
30 | struct clk_mux mux; | ||
31 | const struct clk_ops *ops; | ||
32 | void (*fixup)(u32 *val); | ||
33 | }; | ||
34 | |||
35 | static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw) | ||
36 | { | ||
37 | struct clk_mux *mux = to_clk_mux(hw); | ||
38 | |||
39 | return container_of(mux, struct clk_fixup_mux, mux); | ||
40 | } | ||
41 | |||
42 | static u8 clk_fixup_mux_get_parent(struct clk_hw *hw) | ||
43 | { | ||
44 | struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); | ||
45 | |||
46 | return fixup_mux->ops->get_parent(&fixup_mux->mux.hw); | ||
47 | } | ||
48 | |||
49 | static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index) | ||
50 | { | ||
51 | struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); | ||
52 | struct clk_mux *mux = to_clk_mux(hw); | ||
53 | unsigned long flags = 0; | ||
54 | u32 val; | ||
55 | |||
56 | spin_lock_irqsave(mux->lock, flags); | ||
57 | |||
58 | val = readl(mux->reg); | ||
59 | val &= ~(mux->mask << mux->shift); | ||
60 | val |= index << mux->shift; | ||
61 | fixup_mux->fixup(&val); | ||
62 | writel(val, mux->reg); | ||
63 | |||
64 | spin_unlock_irqrestore(mux->lock, flags); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static const struct clk_ops clk_fixup_mux_ops = { | ||
70 | .get_parent = clk_fixup_mux_get_parent, | ||
71 | .set_parent = clk_fixup_mux_set_parent, | ||
72 | }; | ||
73 | |||
74 | struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, | ||
75 | u8 shift, u8 width, const char **parents, | ||
76 | int num_parents, void (*fixup)(u32 *val)) | ||
77 | { | ||
78 | struct clk_fixup_mux *fixup_mux; | ||
79 | struct clk *clk; | ||
80 | struct clk_init_data init; | ||
81 | |||
82 | if (!fixup) | ||
83 | return ERR_PTR(-EINVAL); | ||
84 | |||
85 | fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL); | ||
86 | if (!fixup_mux) | ||
87 | return ERR_PTR(-ENOMEM); | ||
88 | |||
89 | init.name = name; | ||
90 | init.ops = &clk_fixup_mux_ops; | ||
91 | init.parent_names = parents; | ||
92 | init.num_parents = num_parents; | ||
93 | |||
94 | fixup_mux->mux.reg = reg; | ||
95 | fixup_mux->mux.shift = shift; | ||
96 | fixup_mux->mux.mask = BIT(width) - 1; | ||
97 | fixup_mux->mux.lock = &imx_ccm_lock; | ||
98 | fixup_mux->mux.hw.init = &init; | ||
99 | fixup_mux->ops = &clk_mux_ops; | ||
100 | fixup_mux->fixup = fixup; | ||
101 | |||
102 | clk = clk_register(NULL, &fixup_mux->mux.hw); | ||
103 | if (IS_ERR(clk)) | ||
104 | kfree(fixup_mux); | ||
105 | |||
106 | return clk; | ||
107 | } | ||