diff options
author | Mike Turquette <mturquette@linaro.org> | 2012-03-16 02:11:20 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-03-16 16:35:02 -0400 |
commit | 9d9f78ed9af0e465d2fd15550471956e7f559b9f (patch) | |
tree | 8a65fc0844a35c928162f65b671a8a5786ea03ee /drivers/clk/clk-mux.c | |
parent | b2476490ef11134b65544d8f062cff96c53e941b (diff) |
clk: basic clock hardware types
Many platforms support simple gateable clocks, fixed-rate clocks,
adjustable divider clocks and multi-parent multiplexer clocks.
This patch introduces basic clock types for the above-mentioned hardware
which share some common characteristics.
Based on original work by Jeremy Kerr and contribution by Jamie Iles.
Dividers and multiplexor clocks originally contributed by Richard Zhao &
Sascha Hauer.
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Mike Turquette <mturquette@ti.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Rob Herring <rob.herring@calxeda.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Jeremy Kerr <jeremy.kerr@canonical.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Arnd Bergman <arnd.bergmann@linaro.org>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Shawn Guo <shawn.guo@freescale.com>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Jamie Iles <jamie@jamieiles.com>
Cc: Richard Zhao <richard.zhao@linaro.org>
Cc: Saravana Kannan <skannan@codeaurora.org>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Linus Walleij <linus.walleij@stericsson.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Amit Kucheria <amit.kucheria@linaro.org>
Cc: Deepak Saxena <dsaxena@linaro.org>
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers/clk/clk-mux.c')
-rw-r--r-- | drivers/clk/clk-mux.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c new file mode 100644 index 000000000000..c71ad1f41a97 --- /dev/null +++ b/drivers/clk/clk-mux.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> | ||
3 | * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> | ||
4 | * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Simple multiplexer clock implementation | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/clk-provider.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/err.h> | ||
19 | |||
20 | /* | ||
21 | * DOC: basic adjustable multiplexer clock that cannot gate | ||
22 | * | ||
23 | * Traits of this clock: | ||
24 | * prepare - clk_prepare only ensures that parents are prepared | ||
25 | * enable - clk_enable only ensures that parents are enabled | ||
26 | * rate - rate is only affected by parent switching. No clk_set_rate support | ||
27 | * parent - parent is adjustable through clk_set_parent | ||
28 | */ | ||
29 | |||
30 | #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) | ||
31 | |||
32 | static u8 clk_mux_get_parent(struct clk_hw *hw) | ||
33 | { | ||
34 | struct clk_mux *mux = to_clk_mux(hw); | ||
35 | u32 val; | ||
36 | |||
37 | /* | ||
38 | * FIXME need a mux-specific flag to determine if val is bitwise or numeric | ||
39 | * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 | ||
40 | * to 0x7 (index starts at one) | ||
41 | * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so | ||
42 | * val = 0x4 really means "bit 2, index starts at bit 0" | ||
43 | */ | ||
44 | val = readl(mux->reg) >> mux->shift; | ||
45 | val &= (1 << mux->width) - 1; | ||
46 | |||
47 | if (val && (mux->flags & CLK_MUX_INDEX_BIT)) | ||
48 | val = ffs(val) - 1; | ||
49 | |||
50 | if (val && (mux->flags & CLK_MUX_INDEX_ONE)) | ||
51 | val--; | ||
52 | |||
53 | if (val >= __clk_get_num_parents(hw->clk)) | ||
54 | return -EINVAL; | ||
55 | |||
56 | return val; | ||
57 | } | ||
58 | EXPORT_SYMBOL_GPL(clk_mux_get_parent); | ||
59 | |||
60 | static int clk_mux_set_parent(struct clk_hw *hw, u8 index) | ||
61 | { | ||
62 | struct clk_mux *mux = to_clk_mux(hw); | ||
63 | u32 val; | ||
64 | unsigned long flags = 0; | ||
65 | |||
66 | if (mux->flags & CLK_MUX_INDEX_BIT) | ||
67 | index = (1 << ffs(index)); | ||
68 | |||
69 | if (mux->flags & CLK_MUX_INDEX_ONE) | ||
70 | index++; | ||
71 | |||
72 | if (mux->lock) | ||
73 | spin_lock_irqsave(mux->lock, flags); | ||
74 | |||
75 | val = readl(mux->reg); | ||
76 | val &= ~(((1 << mux->width) - 1) << mux->shift); | ||
77 | val |= index << mux->shift; | ||
78 | writel(val, mux->reg); | ||
79 | |||
80 | if (mux->lock) | ||
81 | spin_unlock_irqrestore(mux->lock, flags); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(clk_mux_set_parent); | ||
86 | |||
87 | struct clk_ops clk_mux_ops = { | ||
88 | .get_parent = clk_mux_get_parent, | ||
89 | .set_parent = clk_mux_set_parent, | ||
90 | }; | ||
91 | EXPORT_SYMBOL_GPL(clk_mux_ops); | ||
92 | |||
93 | struct clk *clk_register_mux(struct device *dev, const char *name, | ||
94 | char **parent_names, u8 num_parents, unsigned long flags, | ||
95 | void __iomem *reg, u8 shift, u8 width, | ||
96 | u8 clk_mux_flags, spinlock_t *lock) | ||
97 | { | ||
98 | struct clk_mux *mux; | ||
99 | |||
100 | mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL); | ||
101 | |||
102 | if (!mux) { | ||
103 | pr_err("%s: could not allocate mux clk\n", __func__); | ||
104 | return ERR_PTR(-ENOMEM); | ||
105 | } | ||
106 | |||
107 | /* struct clk_mux assignments */ | ||
108 | mux->reg = reg; | ||
109 | mux->shift = shift; | ||
110 | mux->width = width; | ||
111 | mux->flags = clk_mux_flags; | ||
112 | mux->lock = lock; | ||
113 | |||
114 | return clk_register(dev, name, &clk_mux_ops, &mux->hw, | ||
115 | parent_names, num_parents, flags); | ||
116 | } | ||