diff options
author | Prashant Gaikwad <pgaikwad@nvidia.com> | 2013-01-11 02:46:20 -0500 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2013-01-28 13:19:07 -0500 |
commit | 8f8f484bf355e546c62c47b8a8c8d19b28787798 (patch) | |
tree | cd64be7c876f3bdc9bcf8d06d7c02304bbffc93e /drivers/clk/tegra/clk-divider.c | |
parent | 9598566721fe7524cca575975ea7e8ea2e27a71b (diff) |
clk: tegra: add Tegra specific clocks
Add Tegra specific clocks, pll, pll_out, peripheral, frac_divider, super.
Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com>
[swarren: alloc sizeof(*foo) not sizeof(struct foo), add comments re:
storing pointers to stack variables, make a timeout loop more idiomatic,
use _clk_pll_disable() not clk_disable_pll() from _program_pll() to
avoid redundant lock operations, unified tegra_clk_periph() and
tegra_clk_periph_nodiv(), unified tegra_clk_pll{,e}, rename all clock
registration functions so they don't have the same name as the clock
structs, return -EINVAL from clk_plle_enable when matching table rate
not found, pass ops to _tegra_clk_register_pll rather than a bool.]
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'drivers/clk/tegra/clk-divider.c')
-rw-r--r-- | drivers/clk/tegra/clk-divider.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c new file mode 100644 index 000000000000..4d75b1f37e3a --- /dev/null +++ b/drivers/clk/tegra/clk-divider.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/clk-provider.h> | ||
22 | #include <linux/clk.h> | ||
23 | |||
24 | #include "clk.h" | ||
25 | |||
26 | #define pll_out_override(p) (BIT((p->shift - 6))) | ||
27 | #define div_mask(d) ((1 << (d->width)) - 1) | ||
28 | #define get_mul(d) (1 << d->frac_width) | ||
29 | #define get_max_div(d) div_mask(d) | ||
30 | |||
31 | #define PERIPH_CLK_UART_DIV_ENB BIT(24) | ||
32 | |||
33 | static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, | ||
34 | unsigned long parent_rate) | ||
35 | { | ||
36 | s64 divider_ux1 = parent_rate; | ||
37 | u8 flags = divider->flags; | ||
38 | int mul; | ||
39 | |||
40 | if (!rate) | ||
41 | return 0; | ||
42 | |||
43 | mul = get_mul(divider); | ||
44 | |||
45 | if (!(flags & TEGRA_DIVIDER_INT)) | ||
46 | divider_ux1 *= mul; | ||
47 | |||
48 | if (flags & TEGRA_DIVIDER_ROUND_UP) | ||
49 | divider_ux1 += rate - 1; | ||
50 | |||
51 | do_div(divider_ux1, rate); | ||
52 | |||
53 | if (flags & TEGRA_DIVIDER_INT) | ||
54 | divider_ux1 *= mul; | ||
55 | |||
56 | divider_ux1 -= mul; | ||
57 | |||
58 | if (divider_ux1 < 0) | ||
59 | return 0; | ||
60 | |||
61 | if (divider_ux1 > get_max_div(divider)) | ||
62 | return -EINVAL; | ||
63 | |||
64 | return divider_ux1; | ||
65 | } | ||
66 | |||
67 | static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, | ||
68 | unsigned long parent_rate) | ||
69 | { | ||
70 | struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
71 | u32 reg; | ||
72 | int div, mul; | ||
73 | u64 rate = parent_rate; | ||
74 | |||
75 | reg = readl_relaxed(divider->reg) >> divider->shift; | ||
76 | div = reg & div_mask(divider); | ||
77 | |||
78 | mul = get_mul(divider); | ||
79 | div += mul; | ||
80 | |||
81 | rate *= mul; | ||
82 | rate += div - 1; | ||
83 | do_div(rate, div); | ||
84 | |||
85 | return rate; | ||
86 | } | ||
87 | |||
88 | static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate, | ||
89 | unsigned long *prate) | ||
90 | { | ||
91 | struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
92 | int div, mul; | ||
93 | unsigned long output_rate = *prate; | ||
94 | |||
95 | if (!rate) | ||
96 | return output_rate; | ||
97 | |||
98 | div = get_div(divider, rate, output_rate); | ||
99 | if (div < 0) | ||
100 | return *prate; | ||
101 | |||
102 | mul = get_mul(divider); | ||
103 | |||
104 | return DIV_ROUND_UP(output_rate * mul, div + mul); | ||
105 | } | ||
106 | |||
107 | static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, | ||
108 | unsigned long parent_rate) | ||
109 | { | ||
110 | struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
111 | int div; | ||
112 | unsigned long flags = 0; | ||
113 | u32 val; | ||
114 | |||
115 | div = get_div(divider, rate, parent_rate); | ||
116 | if (div < 0) | ||
117 | return div; | ||
118 | |||
119 | if (divider->lock) | ||
120 | spin_lock_irqsave(divider->lock, flags); | ||
121 | |||
122 | val = readl_relaxed(divider->reg); | ||
123 | val &= ~(div_mask(divider) << divider->shift); | ||
124 | val |= div << divider->shift; | ||
125 | |||
126 | if (divider->flags & TEGRA_DIVIDER_UART) { | ||
127 | if (div) | ||
128 | val |= PERIPH_CLK_UART_DIV_ENB; | ||
129 | else | ||
130 | val &= ~PERIPH_CLK_UART_DIV_ENB; | ||
131 | } | ||
132 | |||
133 | if (divider->flags & TEGRA_DIVIDER_FIXED) | ||
134 | val |= pll_out_override(divider); | ||
135 | |||
136 | writel_relaxed(val, divider->reg); | ||
137 | |||
138 | if (divider->lock) | ||
139 | spin_unlock_irqrestore(divider->lock, flags); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | const struct clk_ops tegra_clk_frac_div_ops = { | ||
145 | .recalc_rate = clk_frac_div_recalc_rate, | ||
146 | .set_rate = clk_frac_div_set_rate, | ||
147 | .round_rate = clk_frac_div_round_rate, | ||
148 | }; | ||
149 | |||
150 | struct clk *tegra_clk_register_divider(const char *name, | ||
151 | const char *parent_name, void __iomem *reg, | ||
152 | unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, | ||
153 | u8 frac_width, spinlock_t *lock) | ||
154 | { | ||
155 | struct tegra_clk_frac_div *divider; | ||
156 | struct clk *clk; | ||
157 | struct clk_init_data init; | ||
158 | |||
159 | divider = kzalloc(sizeof(*divider), GFP_KERNEL); | ||
160 | if (!divider) { | ||
161 | pr_err("%s: could not allocate fractional divider clk\n", | ||
162 | __func__); | ||
163 | return ERR_PTR(-ENOMEM); | ||
164 | } | ||
165 | |||
166 | init.name = name; | ||
167 | init.ops = &tegra_clk_frac_div_ops; | ||
168 | init.flags = flags; | ||
169 | init.parent_names = parent_name ? &parent_name : NULL; | ||
170 | init.num_parents = parent_name ? 1 : 0; | ||
171 | |||
172 | divider->reg = reg; | ||
173 | divider->shift = shift; | ||
174 | divider->width = width; | ||
175 | divider->frac_width = frac_width; | ||
176 | divider->lock = lock; | ||
177 | divider->flags = clk_divider_flags; | ||
178 | |||
179 | /* Data in .init is copied by clk_register(), so stack variable OK */ | ||
180 | divider->hw.init = &init; | ||
181 | |||
182 | clk = clk_register(NULL, ÷r->hw); | ||
183 | if (IS_ERR(clk)) | ||
184 | kfree(divider); | ||
185 | |||
186 | return clk; | ||
187 | } | ||