diff options
author | Viresh Kumar <viresh.kumar@st.com> | 2012-04-11 08:34:23 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-05-12 15:19:26 -0400 |
commit | 5335a639ecc5646cbe8e99086fb7e743b801ac58 (patch) | |
tree | 812d38780a2eecf385e5f42a4ee3808aa3a85da3 /drivers/clk/spear | |
parent | 55b8fd4f428501b0f35d62b8313311fd9863c188 (diff) |
SPEAr: clk: Add Auxiliary Synthesizer clock
All SPEAr SoC's contain Auxiliary Synthesizers. Their Fout is derived based on
values of eq, x and y.
Fout from synthesizer can be given from two equations:
Fout1 = (Fin * X/Y)/2 EQ1
Fout2 = Fin * X/Y EQ2
This patch adds in support for this type of clock.
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Reviewed-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/spear')
-rw-r--r-- | drivers/clk/spear/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/spear/clk-aux-synth.c | 198 | ||||
-rw-r--r-- | drivers/clk/spear/clk.h | 43 |
3 files changed, 242 insertions, 1 deletions
diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile index 3fc2a3026cc1..e36d8c665724 100644 --- a/drivers/clk/spear/Makefile +++ b/drivers/clk/spear/Makefile | |||
@@ -2,4 +2,4 @@ | |||
2 | # SPEAr Clock specific Makefile | 2 | # SPEAr Clock specific Makefile |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += clk.o clk-vco-pll.o | 5 | obj-y += clk.o clk-aux-synth.o clk-vco-pll.o |
diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c new file mode 100644 index 000000000000..af34074e702b --- /dev/null +++ b/drivers/clk/spear/clk-aux-synth.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 ST Microelectronics | ||
3 | * Viresh Kumar <viresh.kumar@st.com> | ||
4 | * | ||
5 | * This file is licensed under the terms of the GNU General Public | ||
6 | * License version 2. This program is licensed "as is" without any | ||
7 | * warranty of any kind, whether express or implied. | ||
8 | * | ||
9 | * Auxiliary Synthesizer clock implementation | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "clk-aux-synth: " fmt | ||
13 | |||
14 | #include <linux/clk-provider.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/err.h> | ||
18 | #include "clk.h" | ||
19 | |||
20 | /* | ||
21 | * DOC: Auxiliary Synthesizer clock | ||
22 | * | ||
23 | * Aux synth gives rate for different values of eq, x and y | ||
24 | * | ||
25 | * Fout from synthesizer can be given from two equations: | ||
26 | * Fout1 = (Fin * X/Y)/2 EQ1 | ||
27 | * Fout2 = Fin * X/Y EQ2 | ||
28 | */ | ||
29 | |||
30 | #define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) | ||
31 | |||
32 | static struct aux_clk_masks default_aux_masks = { | ||
33 | .eq_sel_mask = AUX_EQ_SEL_MASK, | ||
34 | .eq_sel_shift = AUX_EQ_SEL_SHIFT, | ||
35 | .eq1_mask = AUX_EQ1_SEL, | ||
36 | .eq2_mask = AUX_EQ2_SEL, | ||
37 | .xscale_sel_mask = AUX_XSCALE_MASK, | ||
38 | .xscale_sel_shift = AUX_XSCALE_SHIFT, | ||
39 | .yscale_sel_mask = AUX_YSCALE_MASK, | ||
40 | .yscale_sel_shift = AUX_YSCALE_SHIFT, | ||
41 | .enable_bit = AUX_SYNT_ENB, | ||
42 | }; | ||
43 | |||
44 | static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, | ||
45 | int index) | ||
46 | { | ||
47 | struct clk_aux *aux = to_clk_aux(hw); | ||
48 | struct aux_rate_tbl *rtbl = aux->rtbl; | ||
49 | u8 eq = rtbl[index].eq ? 1 : 2; | ||
50 | |||
51 | return (((prate / 10000) * rtbl[index].xscale) / | ||
52 | (rtbl[index].yscale * eq)) * 10000; | ||
53 | } | ||
54 | |||
55 | static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, | ||
56 | unsigned long *prate) | ||
57 | { | ||
58 | struct clk_aux *aux = to_clk_aux(hw); | ||
59 | int unused; | ||
60 | |||
61 | return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, | ||
62 | aux->rtbl_cnt, &unused); | ||
63 | } | ||
64 | |||
65 | static unsigned long clk_aux_recalc_rate(struct clk_hw *hw, | ||
66 | unsigned long parent_rate) | ||
67 | { | ||
68 | struct clk_aux *aux = to_clk_aux(hw); | ||
69 | unsigned int num = 1, den = 1, val, eqn; | ||
70 | unsigned long flags = 0; | ||
71 | |||
72 | if (aux->lock) | ||
73 | spin_lock_irqsave(aux->lock, flags); | ||
74 | |||
75 | val = readl_relaxed(aux->reg); | ||
76 | |||
77 | if (aux->lock) | ||
78 | spin_unlock_irqrestore(aux->lock, flags); | ||
79 | |||
80 | eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; | ||
81 | if (eqn == aux->masks->eq1_mask) | ||
82 | den = 2; | ||
83 | |||
84 | /* calculate numerator */ | ||
85 | num = (val >> aux->masks->xscale_sel_shift) & | ||
86 | aux->masks->xscale_sel_mask; | ||
87 | |||
88 | /* calculate denominator */ | ||
89 | den *= (val >> aux->masks->yscale_sel_shift) & | ||
90 | aux->masks->yscale_sel_mask; | ||
91 | |||
92 | if (!den) | ||
93 | return 0; | ||
94 | |||
95 | return (((parent_rate / 10000) * num) / den) * 10000; | ||
96 | } | ||
97 | |||
98 | /* Configures new clock rate of aux */ | ||
99 | static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, | ||
100 | unsigned long prate) | ||
101 | { | ||
102 | struct clk_aux *aux = to_clk_aux(hw); | ||
103 | struct aux_rate_tbl *rtbl = aux->rtbl; | ||
104 | unsigned long val, flags = 0; | ||
105 | int i; | ||
106 | |||
107 | clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, | ||
108 | &i); | ||
109 | |||
110 | if (aux->lock) | ||
111 | spin_lock_irqsave(aux->lock, flags); | ||
112 | |||
113 | val = readl_relaxed(aux->reg) & | ||
114 | ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); | ||
115 | val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << | ||
116 | aux->masks->eq_sel_shift; | ||
117 | val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); | ||
118 | val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << | ||
119 | aux->masks->xscale_sel_shift; | ||
120 | val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); | ||
121 | val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << | ||
122 | aux->masks->yscale_sel_shift; | ||
123 | writel_relaxed(val, aux->reg); | ||
124 | |||
125 | if (aux->lock) | ||
126 | spin_unlock_irqrestore(aux->lock, flags); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static struct clk_ops clk_aux_ops = { | ||
132 | .recalc_rate = clk_aux_recalc_rate, | ||
133 | .round_rate = clk_aux_round_rate, | ||
134 | .set_rate = clk_aux_set_rate, | ||
135 | }; | ||
136 | |||
137 | struct clk *clk_register_aux(const char *aux_name, const char *gate_name, | ||
138 | const char *parent_name, unsigned long flags, void __iomem *reg, | ||
139 | struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, | ||
140 | u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) | ||
141 | { | ||
142 | struct clk_aux *aux; | ||
143 | struct clk_init_data init; | ||
144 | struct clk *clk; | ||
145 | |||
146 | if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { | ||
147 | pr_err("Invalid arguments passed"); | ||
148 | return ERR_PTR(-EINVAL); | ||
149 | } | ||
150 | |||
151 | aux = kzalloc(sizeof(*aux), GFP_KERNEL); | ||
152 | if (!aux) { | ||
153 | pr_err("could not allocate aux clk\n"); | ||
154 | return ERR_PTR(-ENOMEM); | ||
155 | } | ||
156 | |||
157 | /* struct clk_aux assignments */ | ||
158 | if (!masks) | ||
159 | aux->masks = &default_aux_masks; | ||
160 | else | ||
161 | aux->masks = masks; | ||
162 | |||
163 | aux->reg = reg; | ||
164 | aux->rtbl = rtbl; | ||
165 | aux->rtbl_cnt = rtbl_cnt; | ||
166 | aux->lock = lock; | ||
167 | aux->hw.init = &init; | ||
168 | |||
169 | init.name = aux_name; | ||
170 | init.ops = &clk_aux_ops; | ||
171 | init.flags = flags; | ||
172 | init.parent_names = &parent_name; | ||
173 | init.num_parents = 1; | ||
174 | |||
175 | clk = clk_register(NULL, &aux->hw); | ||
176 | if (IS_ERR_OR_NULL(clk)) | ||
177 | goto free_aux; | ||
178 | |||
179 | if (gate_name) { | ||
180 | struct clk *tgate_clk; | ||
181 | |||
182 | tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 0, reg, | ||
183 | aux->masks->enable_bit, 0, lock); | ||
184 | if (IS_ERR_OR_NULL(tgate_clk)) | ||
185 | goto free_aux; | ||
186 | |||
187 | if (gate_clk) | ||
188 | *gate_clk = tgate_clk; | ||
189 | } | ||
190 | |||
191 | return clk; | ||
192 | |||
193 | free_aux: | ||
194 | kfree(aux); | ||
195 | pr_err("clk register failed\n"); | ||
196 | |||
197 | return NULL; | ||
198 | } | ||
diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index 9979b7f7e767..c2290800503a 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h | |||
@@ -16,6 +16,45 @@ | |||
16 | #include <linux/spinlock_types.h> | 16 | #include <linux/spinlock_types.h> |
17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
18 | 18 | ||
19 | /* Auxiliary Synth clk */ | ||
20 | /* Default masks */ | ||
21 | #define AUX_EQ_SEL_SHIFT 30 | ||
22 | #define AUX_EQ_SEL_MASK 1 | ||
23 | #define AUX_EQ1_SEL 0 | ||
24 | #define AUX_EQ2_SEL 1 | ||
25 | #define AUX_XSCALE_SHIFT 16 | ||
26 | #define AUX_XSCALE_MASK 0xFFF | ||
27 | #define AUX_YSCALE_SHIFT 0 | ||
28 | #define AUX_YSCALE_MASK 0xFFF | ||
29 | #define AUX_SYNT_ENB 31 | ||
30 | |||
31 | struct aux_clk_masks { | ||
32 | u32 eq_sel_mask; | ||
33 | u32 eq_sel_shift; | ||
34 | u32 eq1_mask; | ||
35 | u32 eq2_mask; | ||
36 | u32 xscale_sel_mask; | ||
37 | u32 xscale_sel_shift; | ||
38 | u32 yscale_sel_mask; | ||
39 | u32 yscale_sel_shift; | ||
40 | u32 enable_bit; | ||
41 | }; | ||
42 | |||
43 | struct aux_rate_tbl { | ||
44 | u16 xscale; | ||
45 | u16 yscale; | ||
46 | u8 eq; | ||
47 | }; | ||
48 | |||
49 | struct clk_aux { | ||
50 | struct clk_hw hw; | ||
51 | void __iomem *reg; | ||
52 | struct aux_clk_masks *masks; | ||
53 | struct aux_rate_tbl *rtbl; | ||
54 | u8 rtbl_cnt; | ||
55 | spinlock_t *lock; | ||
56 | }; | ||
57 | |||
19 | /* VCO-PLL clk */ | 58 | /* VCO-PLL clk */ |
20 | struct pll_rate_tbl { | 59 | struct pll_rate_tbl { |
21 | u8 mode; | 60 | u8 mode; |
@@ -44,6 +83,10 @@ typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, | |||
44 | int index); | 83 | int index); |
45 | 84 | ||
46 | /* clk register routines */ | 85 | /* clk register routines */ |
86 | struct clk *clk_register_aux(const char *aux_name, const char *gate_name, | ||
87 | const char *parent_name, unsigned long flags, void __iomem *reg, | ||
88 | struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, | ||
89 | u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); | ||
47 | struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, | 90 | struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, |
48 | const char *vco_gate_name, const char *parent_name, | 91 | const char *vco_gate_name, const char *parent_name, |
49 | unsigned long flags, void __iomem *mode_reg, void __iomem | 92 | unsigned long flags, void __iomem *mode_reg, void __iomem |