]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/clk/tegra/clk-periph.c
ARM: SPEAr: conditionalize SMP code
[karo-tx-linux.git] / drivers / clk / tegra / clk-periph.c
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/clk.h>
18 #include <linux/clk-provider.h>
19 #include <linux/slab.h>
20 #include <linux/err.h>
21
22 #include "clk.h"
23
24 static u8 clk_periph_get_parent(struct clk_hw *hw)
25 {
26         struct tegra_clk_periph *periph = to_clk_periph(hw);
27         const struct clk_ops *mux_ops = periph->mux_ops;
28         struct clk_hw *mux_hw = &periph->mux.hw;
29
30         mux_hw->clk = hw->clk;
31
32         return mux_ops->get_parent(mux_hw);
33 }
34
35 static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
36 {
37         struct tegra_clk_periph *periph = to_clk_periph(hw);
38         const struct clk_ops *mux_ops = periph->mux_ops;
39         struct clk_hw *mux_hw = &periph->mux.hw;
40
41         mux_hw->clk = hw->clk;
42
43         return mux_ops->set_parent(mux_hw, index);
44 }
45
46 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
47                                             unsigned long parent_rate)
48 {
49         struct tegra_clk_periph *periph = to_clk_periph(hw);
50         const struct clk_ops *div_ops = periph->div_ops;
51         struct clk_hw *div_hw = &periph->divider.hw;
52
53         div_hw->clk = hw->clk;
54
55         return div_ops->recalc_rate(div_hw, parent_rate);
56 }
57
58 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
59                                   unsigned long *prate)
60 {
61         struct tegra_clk_periph *periph = to_clk_periph(hw);
62         const struct clk_ops *div_ops = periph->div_ops;
63         struct clk_hw *div_hw = &periph->divider.hw;
64
65         div_hw->clk = hw->clk;
66
67         return div_ops->round_rate(div_hw, rate, prate);
68 }
69
70 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
71                                unsigned long parent_rate)
72 {
73         struct tegra_clk_periph *periph = to_clk_periph(hw);
74         const struct clk_ops *div_ops = periph->div_ops;
75         struct clk_hw *div_hw = &periph->divider.hw;
76
77         div_hw->clk = hw->clk;
78
79         return div_ops->set_rate(div_hw, rate, parent_rate);
80 }
81
82 static int clk_periph_is_enabled(struct clk_hw *hw)
83 {
84         struct tegra_clk_periph *periph = to_clk_periph(hw);
85         const struct clk_ops *gate_ops = periph->gate_ops;
86         struct clk_hw *gate_hw = &periph->gate.hw;
87
88         gate_hw->clk = hw->clk;
89
90         return gate_ops->is_enabled(gate_hw);
91 }
92
93 static int clk_periph_enable(struct clk_hw *hw)
94 {
95         struct tegra_clk_periph *periph = to_clk_periph(hw);
96         const struct clk_ops *gate_ops = periph->gate_ops;
97         struct clk_hw *gate_hw = &periph->gate.hw;
98
99         gate_hw->clk = hw->clk;
100
101         return gate_ops->enable(gate_hw);
102 }
103
104 static void clk_periph_disable(struct clk_hw *hw)
105 {
106         struct tegra_clk_periph *periph = to_clk_periph(hw);
107         const struct clk_ops *gate_ops = periph->gate_ops;
108         struct clk_hw *gate_hw = &periph->gate.hw;
109
110         gate_ops->disable(gate_hw);
111 }
112
113 void tegra_periph_reset_deassert(struct clk *c)
114 {
115         struct clk_hw *hw = __clk_get_hw(c);
116         struct tegra_clk_periph *periph = to_clk_periph(hw);
117         struct tegra_clk_periph_gate *gate;
118
119         if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
120                 gate = to_clk_periph_gate(hw);
121                 if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
122                         WARN_ON(1);
123                         return;
124                 }
125         } else {
126                 gate = &periph->gate;
127         }
128
129         tegra_periph_reset(gate, 0);
130 }
131
132 void tegra_periph_reset_assert(struct clk *c)
133 {
134         struct clk_hw *hw = __clk_get_hw(c);
135         struct tegra_clk_periph *periph = to_clk_periph(hw);
136         struct tegra_clk_periph_gate *gate;
137
138         if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
139                 gate = to_clk_periph_gate(hw);
140                 if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
141                         WARN_ON(1);
142                         return;
143                 }
144         } else {
145                 gate = &periph->gate;
146         }
147
148         tegra_periph_reset(gate, 1);
149 }
150
151 const struct clk_ops tegra_clk_periph_ops = {
152         .get_parent = clk_periph_get_parent,
153         .set_parent = clk_periph_set_parent,
154         .recalc_rate = clk_periph_recalc_rate,
155         .round_rate = clk_periph_round_rate,
156         .set_rate = clk_periph_set_rate,
157         .is_enabled = clk_periph_is_enabled,
158         .enable = clk_periph_enable,
159         .disable = clk_periph_disable,
160 };
161
162 const struct clk_ops tegra_clk_periph_nodiv_ops = {
163         .get_parent = clk_periph_get_parent,
164         .set_parent = clk_periph_set_parent,
165         .is_enabled = clk_periph_is_enabled,
166         .enable = clk_periph_enable,
167         .disable = clk_periph_disable,
168 };
169
170 static struct clk *_tegra_clk_register_periph(const char *name,
171                         const char **parent_names, int num_parents,
172                         struct tegra_clk_periph *periph,
173                         void __iomem *clk_base, u32 offset, bool div)
174 {
175         struct clk *clk;
176         struct clk_init_data init;
177
178         init.name = name;
179         init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
180         init.flags = div ? 0 : CLK_SET_RATE_PARENT;
181         init.parent_names = parent_names;
182         init.num_parents = num_parents;
183
184         /* Data in .init is copied by clk_register(), so stack variable OK */
185         periph->hw.init = &init;
186         periph->magic = TEGRA_CLK_PERIPH_MAGIC;
187         periph->mux.reg = clk_base + offset;
188         periph->divider.reg = div ? (clk_base + offset) : NULL;
189         periph->gate.clk_base = clk_base;
190
191         clk = clk_register(NULL, &periph->hw);
192         if (IS_ERR(clk))
193                 return clk;
194
195         periph->mux.hw.clk = clk;
196         periph->divider.hw.clk = div ? clk : NULL;
197         periph->gate.hw.clk = clk;
198
199         return clk;
200 }
201
202 struct clk *tegra_clk_register_periph(const char *name,
203                 const char **parent_names, int num_parents,
204                 struct tegra_clk_periph *periph, void __iomem *clk_base,
205                 u32 offset)
206 {
207         return _tegra_clk_register_periph(name, parent_names, num_parents,
208                         periph, clk_base, offset, true);
209 }
210
211 struct clk *tegra_clk_register_periph_nodiv(const char *name,
212                 const char **parent_names, int num_parents,
213                 struct tegra_clk_periph *periph, void __iomem *clk_base,
214                 u32 offset)
215 {
216         return _tegra_clk_register_periph(name, parent_names, num_parents,
217                         periph, clk_base, offset, false);
218 }