2 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
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.
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
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/>.
17 #include <linux/clk.h>
18 #include <linux/clk-provider.h>
19 #include <linux/export.h>
20 #include <linux/slab.h>
21 #include <linux/err.h>
25 static u8
clk_periph_get_parent(struct clk_hw
*hw
)
27 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
28 const struct clk_ops
*mux_ops
= periph
->mux_ops
;
29 struct clk_hw
*mux_hw
= &periph
->mux
.hw
;
31 mux_hw
->clk
= hw
->clk
;
33 return mux_ops
->get_parent(mux_hw
);
36 static int clk_periph_set_parent(struct clk_hw
*hw
, u8 index
)
38 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
39 const struct clk_ops
*mux_ops
= periph
->mux_ops
;
40 struct clk_hw
*mux_hw
= &periph
->mux
.hw
;
42 mux_hw
->clk
= hw
->clk
;
44 return mux_ops
->set_parent(mux_hw
, index
);
47 static unsigned long clk_periph_recalc_rate(struct clk_hw
*hw
,
48 unsigned long parent_rate
)
50 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
51 const struct clk_ops
*div_ops
= periph
->div_ops
;
52 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
54 div_hw
->clk
= hw
->clk
;
56 return div_ops
->recalc_rate(div_hw
, parent_rate
);
59 static long clk_periph_round_rate(struct clk_hw
*hw
, unsigned long rate
,
62 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
63 const struct clk_ops
*div_ops
= periph
->div_ops
;
64 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
66 div_hw
->clk
= hw
->clk
;
68 return div_ops
->round_rate(div_hw
, rate
, prate
);
71 static int clk_periph_set_rate(struct clk_hw
*hw
, unsigned long rate
,
72 unsigned long parent_rate
)
74 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
75 const struct clk_ops
*div_ops
= periph
->div_ops
;
76 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
78 div_hw
->clk
= hw
->clk
;
80 return div_ops
->set_rate(div_hw
, rate
, parent_rate
);
83 static int clk_periph_is_enabled(struct clk_hw
*hw
)
85 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
86 const struct clk_ops
*gate_ops
= periph
->gate_ops
;
87 struct clk_hw
*gate_hw
= &periph
->gate
.hw
;
89 gate_hw
->clk
= hw
->clk
;
91 return gate_ops
->is_enabled(gate_hw
);
94 static int clk_periph_enable(struct clk_hw
*hw
)
96 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
97 const struct clk_ops
*gate_ops
= periph
->gate_ops
;
98 struct clk_hw
*gate_hw
= &periph
->gate
.hw
;
100 gate_hw
->clk
= hw
->clk
;
102 return gate_ops
->enable(gate_hw
);
105 static void clk_periph_disable(struct clk_hw
*hw
)
107 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
108 const struct clk_ops
*gate_ops
= periph
->gate_ops
;
109 struct clk_hw
*gate_hw
= &periph
->gate
.hw
;
111 gate_ops
->disable(gate_hw
);
114 void tegra_periph_reset_deassert(struct clk
*c
)
116 struct clk_hw
*hw
= __clk_get_hw(c
);
117 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
118 struct tegra_clk_periph_gate
*gate
;
120 if (periph
->magic
!= TEGRA_CLK_PERIPH_MAGIC
) {
121 gate
= to_clk_periph_gate(hw
);
122 if (gate
->magic
!= TEGRA_CLK_PERIPH_GATE_MAGIC
) {
127 gate
= &periph
->gate
;
130 tegra_periph_reset(gate
, 0);
132 EXPORT_SYMBOL(tegra_periph_reset_deassert
);
134 void tegra_periph_reset_assert(struct clk
*c
)
136 struct clk_hw
*hw
= __clk_get_hw(c
);
137 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
138 struct tegra_clk_periph_gate
*gate
;
140 if (periph
->magic
!= TEGRA_CLK_PERIPH_MAGIC
) {
141 gate
= to_clk_periph_gate(hw
);
142 if (gate
->magic
!= TEGRA_CLK_PERIPH_GATE_MAGIC
) {
147 gate
= &periph
->gate
;
150 tegra_periph_reset(gate
, 1);
152 EXPORT_SYMBOL(tegra_periph_reset_assert
);
154 const struct clk_ops tegra_clk_periph_ops
= {
155 .get_parent
= clk_periph_get_parent
,
156 .set_parent
= clk_periph_set_parent
,
157 .recalc_rate
= clk_periph_recalc_rate
,
158 .round_rate
= clk_periph_round_rate
,
159 .set_rate
= clk_periph_set_rate
,
160 .is_enabled
= clk_periph_is_enabled
,
161 .enable
= clk_periph_enable
,
162 .disable
= clk_periph_disable
,
165 const struct clk_ops tegra_clk_periph_nodiv_ops
= {
166 .get_parent
= clk_periph_get_parent
,
167 .set_parent
= clk_periph_set_parent
,
168 .is_enabled
= clk_periph_is_enabled
,
169 .enable
= clk_periph_enable
,
170 .disable
= clk_periph_disable
,
173 static struct clk
*_tegra_clk_register_periph(const char *name
,
174 const char **parent_names
, int num_parents
,
175 struct tegra_clk_periph
*periph
,
176 void __iomem
*clk_base
, u32 offset
, bool div
,
180 struct clk_init_data init
;
183 init
.ops
= div
? &tegra_clk_periph_ops
: &tegra_clk_periph_nodiv_ops
;
185 init
.parent_names
= parent_names
;
186 init
.num_parents
= num_parents
;
188 /* Data in .init is copied by clk_register(), so stack variable OK */
189 periph
->hw
.init
= &init
;
190 periph
->magic
= TEGRA_CLK_PERIPH_MAGIC
;
191 periph
->mux
.reg
= clk_base
+ offset
;
192 periph
->divider
.reg
= div
? (clk_base
+ offset
) : NULL
;
193 periph
->gate
.clk_base
= clk_base
;
195 clk
= clk_register(NULL
, &periph
->hw
);
199 periph
->mux
.hw
.clk
= clk
;
200 periph
->divider
.hw
.clk
= div
? clk
: NULL
;
201 periph
->gate
.hw
.clk
= clk
;
206 struct clk
*tegra_clk_register_periph(const char *name
,
207 const char **parent_names
, int num_parents
,
208 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
209 u32 offset
, unsigned long flags
)
211 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
212 periph
, clk_base
, offset
, true, flags
);
215 struct clk
*tegra_clk_register_periph_nodiv(const char *name
,
216 const char **parent_names
, int num_parents
,
217 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
220 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
221 periph
, clk_base
, offset
, false, CLK_SET_RATE_PARENT
);