aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorMike Turquette <mturquette@linaro.org>2012-03-16 02:11:20 -0400
committerArnd Bergmann <arnd@arndb.de>2012-03-16 16:35:02 -0400
commit9d9f78ed9af0e465d2fd15550471956e7f559b9f (patch)
tree8a65fc0844a35c928162f65b671a8a5786ea03ee /drivers/clk
parentb2476490ef11134b65544d8f062cff96c53e941b (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')
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/clk-divider.c200
-rw-r--r--drivers/clk/clk-fixed-rate.c82
-rw-r--r--drivers/clk/clk-gate.c150
-rw-r--r--drivers/clk/clk-mux.c116
5 files changed, 550 insertions, 1 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index ff362c4fb4c3..1f736bc11c4b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,3 +1,4 @@
1 1
2obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o 2obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
3obj-$(CONFIG_COMMON_CLK) += clk.o 3obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
4 clk-mux.o clk-divider.o
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 000000000000..d5ac6a75ea57
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,200 @@
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 * Adjustable divider clock implementation
11 */
12
13#include <linux/clk-provider.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/io.h>
17#include <linux/err.h>
18#include <linux/string.h>
19
20/*
21 * DOC: basic adjustable divider 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 adjustable. clk->rate = parent->rate / divisor
27 * parent - fixed parent. No clk_set_parent support
28 */
29
30#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
31
32#define div_mask(d) ((1 << (d->width)) - 1)
33
34static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
35 unsigned long parent_rate)
36{
37 struct clk_divider *divider = to_clk_divider(hw);
38 unsigned int div;
39
40 div = readl(divider->reg) >> divider->shift;
41 div &= div_mask(divider);
42
43 if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
44 div++;
45
46 return parent_rate / div;
47}
48EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
49
50/*
51 * The reverse of DIV_ROUND_UP: The maximum number which
52 * divided by m is r
53 */
54#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
55
56static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
57 unsigned long *best_parent_rate)
58{
59 struct clk_divider *divider = to_clk_divider(hw);
60 int i, bestdiv = 0;
61 unsigned long parent_rate, best = 0, now, maxdiv;
62
63 if (!rate)
64 rate = 1;
65
66 maxdiv = (1 << divider->width);
67
68 if (divider->flags & CLK_DIVIDER_ONE_BASED)
69 maxdiv--;
70
71 if (!best_parent_rate) {
72 parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
73 bestdiv = DIV_ROUND_UP(parent_rate, rate);
74 bestdiv = bestdiv == 0 ? 1 : bestdiv;
75 bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
76 return bestdiv;
77 }
78
79 /*
80 * The maximum divider we can use without overflowing
81 * unsigned long in rate * i below
82 */
83 maxdiv = min(ULONG_MAX / rate, maxdiv);
84
85 for (i = 1; i <= maxdiv; i++) {
86 parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
87 MULT_ROUND_UP(rate, i));
88 now = parent_rate / i;
89 if (now <= rate && now > best) {
90 bestdiv = i;
91 best = now;
92 *best_parent_rate = parent_rate;
93 }
94 }
95
96 if (!bestdiv) {
97 bestdiv = (1 << divider->width);
98 if (divider->flags & CLK_DIVIDER_ONE_BASED)
99 bestdiv--;
100 *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
101 }
102
103 return bestdiv;
104}
105
106static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
107 unsigned long *prate)
108{
109 int div;
110 div = clk_divider_bestdiv(hw, rate, prate);
111
112 if (prate)
113 return *prate / div;
114 else {
115 unsigned long r;
116 r = __clk_get_rate(__clk_get_parent(hw->clk));
117 return r / div;
118 }
119}
120EXPORT_SYMBOL_GPL(clk_divider_round_rate);
121
122static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate)
123{
124 struct clk_divider *divider = to_clk_divider(hw);
125 unsigned int div;
126 unsigned long flags = 0;
127 u32 val;
128
129 div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate;
130
131 if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
132 div--;
133
134 if (div > div_mask(divider))
135 div = div_mask(divider);
136
137 if (divider->lock)
138 spin_lock_irqsave(divider->lock, flags);
139
140 val = readl(divider->reg);
141 val &= ~(div_mask(divider) << divider->shift);
142 val |= div << divider->shift;
143 writel(val, divider->reg);
144
145 if (divider->lock)
146 spin_unlock_irqrestore(divider->lock, flags);
147
148 return 0;
149}
150EXPORT_SYMBOL_GPL(clk_divider_set_rate);
151
152struct clk_ops clk_divider_ops = {
153 .recalc_rate = clk_divider_recalc_rate,
154 .round_rate = clk_divider_round_rate,
155 .set_rate = clk_divider_set_rate,
156};
157EXPORT_SYMBOL_GPL(clk_divider_ops);
158
159struct clk *clk_register_divider(struct device *dev, const char *name,
160 const char *parent_name, unsigned long flags,
161 void __iomem *reg, u8 shift, u8 width,
162 u8 clk_divider_flags, spinlock_t *lock)
163{
164 struct clk_divider *div;
165 struct clk *clk;
166
167 div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
168
169 if (!div) {
170 pr_err("%s: could not allocate divider clk\n", __func__);
171 return NULL;
172 }
173
174 /* struct clk_divider assignments */
175 div->reg = reg;
176 div->shift = shift;
177 div->width = width;
178 div->flags = clk_divider_flags;
179 div->lock = lock;
180
181 if (parent_name) {
182 div->parent[0] = kstrdup(parent_name, GFP_KERNEL);
183 if (!div->parent[0])
184 goto out;
185 }
186
187 clk = clk_register(dev, name,
188 &clk_divider_ops, &div->hw,
189 div->parent,
190 (parent_name ? 1 : 0),
191 flags);
192 if (clk)
193 return clk;
194
195out:
196 kfree(div->parent[0]);
197 kfree(div);
198
199 return NULL;
200}
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
new file mode 100644
index 000000000000..90c79fb5d1bd
--- /dev/null
+++ b/drivers/clk/clk-fixed-rate.c
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
3 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Fixed rate clock implementation
10 */
11
12#include <linux/clk-provider.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/err.h>
17
18/*
19 * DOC: basic fixed-rate clock that cannot gate
20 *
21 * Traits of this clock:
22 * prepare - clk_(un)prepare only ensures parents are prepared
23 * enable - clk_enable only ensures parents are enabled
24 * rate - rate is always a fixed value. No clk_set_rate support
25 * parent - fixed parent. No clk_set_parent support
26 */
27
28#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
29
30static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
31 unsigned long parent_rate)
32{
33 return to_clk_fixed_rate(hw)->fixed_rate;
34}
35EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate);
36
37struct clk_ops clk_fixed_rate_ops = {
38 .recalc_rate = clk_fixed_rate_recalc_rate,
39};
40EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
41
42struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
43 const char *parent_name, unsigned long flags,
44 unsigned long fixed_rate)
45{
46 struct clk_fixed_rate *fixed;
47 char **parent_names = NULL;
48 u8 len;
49
50 fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
51
52 if (!fixed) {
53 pr_err("%s: could not allocate fixed clk\n", __func__);
54 return ERR_PTR(-ENOMEM);
55 }
56
57 /* struct clk_fixed_rate assignments */
58 fixed->fixed_rate = fixed_rate;
59
60 if (parent_name) {
61 parent_names = kmalloc(sizeof(char *), GFP_KERNEL);
62
63 if (! parent_names)
64 goto out;
65
66 len = sizeof(char) * strlen(parent_name);
67
68 parent_names[0] = kmalloc(len, GFP_KERNEL);
69
70 if (!parent_names[0])
71 goto out;
72
73 strncpy(parent_names[0], parent_name, len);
74 }
75
76out:
77 return clk_register(dev, name,
78 &clk_fixed_rate_ops, &fixed->hw,
79 parent_names,
80 (parent_name ? 1 : 0),
81 flags);
82}
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
new file mode 100644
index 000000000000..b5902e2ef2fd
--- /dev/null
+++ b/drivers/clk/clk-gate.c
@@ -0,0 +1,150 @@
1/*
2 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
3 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Gated clock implementation
10 */
11
12#include <linux/clk-provider.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/err.h>
17#include <linux/string.h>
18
19/**
20 * DOC: basic gatable clock which can gate and ungate it's ouput
21 *
22 * Traits of this clock:
23 * prepare - clk_(un)prepare only ensures parent is (un)prepared
24 * enable - clk_enable and clk_disable are functional & control gating
25 * rate - inherits rate from parent. No clk_set_rate support
26 * parent - fixed parent. No clk_set_parent support
27 */
28
29#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
30
31static void clk_gate_set_bit(struct clk_gate *gate)
32{
33 u32 reg;
34 unsigned long flags = 0;
35
36 if (gate->lock)
37 spin_lock_irqsave(gate->lock, flags);
38
39 reg = readl(gate->reg);
40 reg |= BIT(gate->bit_idx);
41 writel(reg, gate->reg);
42
43 if (gate->lock)
44 spin_unlock_irqrestore(gate->lock, flags);
45}
46
47static void clk_gate_clear_bit(struct clk_gate *gate)
48{
49 u32 reg;
50 unsigned long flags = 0;
51
52 if (gate->lock)
53 spin_lock_irqsave(gate->lock, flags);
54
55 reg = readl(gate->reg);
56 reg &= ~BIT(gate->bit_idx);
57 writel(reg, gate->reg);
58
59 if (gate->lock)
60 spin_unlock_irqrestore(gate->lock, flags);
61}
62
63static int clk_gate_enable(struct clk_hw *hw)
64{
65 struct clk_gate *gate = to_clk_gate(hw);
66
67 if (gate->flags & CLK_GATE_SET_TO_DISABLE)
68 clk_gate_clear_bit(gate);
69 else
70 clk_gate_set_bit(gate);
71
72 return 0;
73}
74EXPORT_SYMBOL_GPL(clk_gate_enable);
75
76static void clk_gate_disable(struct clk_hw *hw)
77{
78 struct clk_gate *gate = to_clk_gate(hw);
79
80 if (gate->flags & CLK_GATE_SET_TO_DISABLE)
81 clk_gate_set_bit(gate);
82 else
83 clk_gate_clear_bit(gate);
84}
85EXPORT_SYMBOL_GPL(clk_gate_disable);
86
87static int clk_gate_is_enabled(struct clk_hw *hw)
88{
89 u32 reg;
90 struct clk_gate *gate = to_clk_gate(hw);
91
92 reg = readl(gate->reg);
93
94 /* if a set bit disables this clk, flip it before masking */
95 if (gate->flags & CLK_GATE_SET_TO_DISABLE)
96 reg ^= BIT(gate->bit_idx);
97
98 reg &= BIT(gate->bit_idx);
99
100 return reg ? 1 : 0;
101}
102EXPORT_SYMBOL_GPL(clk_gate_is_enabled);
103
104struct clk_ops clk_gate_ops = {
105 .enable = clk_gate_enable,
106 .disable = clk_gate_disable,
107 .is_enabled = clk_gate_is_enabled,
108};
109EXPORT_SYMBOL_GPL(clk_gate_ops);
110
111struct clk *clk_register_gate(struct device *dev, const char *name,
112 const char *parent_name, unsigned long flags,
113 void __iomem *reg, u8 bit_idx,
114 u8 clk_gate_flags, spinlock_t *lock)
115{
116 struct clk_gate *gate;
117 struct clk *clk;
118
119 gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
120
121 if (!gate) {
122 pr_err("%s: could not allocate gated clk\n", __func__);
123 return NULL;
124 }
125
126 /* struct clk_gate assignments */
127 gate->reg = reg;
128 gate->bit_idx = bit_idx;
129 gate->flags = clk_gate_flags;
130 gate->lock = lock;
131
132 if (parent_name) {
133 gate->parent[0] = kstrdup(parent_name, GFP_KERNEL);
134 if (!gate->parent[0])
135 goto out;
136 }
137
138 clk = clk_register(dev, name,
139 &clk_gate_ops, &gate->hw,
140 gate->parent,
141 (parent_name ? 1 : 0),
142 flags);
143 if (clk)
144 return clk;
145out:
146 kfree(gate->parent[0]);
147 kfree(gate);
148
149 return NULL;
150}
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
32static 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}
58EXPORT_SYMBOL_GPL(clk_mux_get_parent);
59
60static 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}
85EXPORT_SYMBOL_GPL(clk_mux_set_parent);
86
87struct clk_ops clk_mux_ops = {
88 .get_parent = clk_mux_get_parent,
89 .set_parent = clk_mux_set_parent,
90};
91EXPORT_SYMBOL_GPL(clk_mux_ops);
92
93struct 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}