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 const struct clk_ops tegra_clk_periph_ops
= {
115 .get_parent
= clk_periph_get_parent
,
116 .set_parent
= clk_periph_set_parent
,
117 .recalc_rate
= clk_periph_recalc_rate
,
118 .round_rate
= clk_periph_round_rate
,
119 .set_rate
= clk_periph_set_rate
,
120 .is_enabled
= clk_periph_is_enabled
,
121 .enable
= clk_periph_enable
,
122 .disable
= clk_periph_disable
,
125 static const struct clk_ops tegra_clk_periph_nodiv_ops
= {
126 .get_parent
= clk_periph_get_parent
,
127 .set_parent
= clk_periph_set_parent
,
128 .is_enabled
= clk_periph_is_enabled
,
129 .enable
= clk_periph_enable
,
130 .disable
= clk_periph_disable
,
133 static const struct clk_ops tegra_clk_periph_no_gate_ops
= {
134 .get_parent
= clk_periph_get_parent
,
135 .set_parent
= clk_periph_set_parent
,
136 .recalc_rate
= clk_periph_recalc_rate
,
137 .round_rate
= clk_periph_round_rate
,
138 .set_rate
= clk_periph_set_rate
,
141 static struct clk
*_tegra_clk_register_periph(const char *name
,
142 const char **parent_names
, int num_parents
,
143 struct tegra_clk_periph
*periph
,
144 void __iomem
*clk_base
, u32 offset
,
148 struct clk_init_data init
;
149 struct tegra_clk_periph_regs
*bank
;
150 bool div
= !(periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
);
152 if (periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
) {
153 flags
|= CLK_SET_RATE_PARENT
;
154 init
.ops
= &tegra_clk_periph_nodiv_ops
;
155 } else if (periph
->gate
.flags
& TEGRA_PERIPH_NO_GATE
)
156 init
.ops
= &tegra_clk_periph_no_gate_ops
;
158 init
.ops
= &tegra_clk_periph_ops
;
162 init
.parent_names
= parent_names
;
163 init
.num_parents
= num_parents
;
165 bank
= get_reg_bank(periph
->gate
.clk_num
);
167 return ERR_PTR(-EINVAL
);
169 /* Data in .init is copied by clk_register(), so stack variable OK */
170 periph
->hw
.init
= &init
;
171 periph
->magic
= TEGRA_CLK_PERIPH_MAGIC
;
172 periph
->mux
.reg
= clk_base
+ offset
;
173 periph
->divider
.reg
= div
? (clk_base
+ offset
) : NULL
;
174 periph
->gate
.clk_base
= clk_base
;
175 periph
->gate
.regs
= bank
;
176 periph
->gate
.enable_refcnt
= periph_clk_enb_refcnt
;
178 clk
= clk_register(NULL
, &periph
->hw
);
182 periph
->mux
.hw
.clk
= clk
;
183 periph
->divider
.hw
.clk
= div
? clk
: NULL
;
184 periph
->gate
.hw
.clk
= clk
;
189 struct clk
*tegra_clk_register_periph(const char *name
,
190 const char **parent_names
, int num_parents
,
191 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
192 u32 offset
, unsigned long flags
)
194 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
195 periph
, clk_base
, offset
, flags
);
198 struct clk
*tegra_clk_register_periph_nodiv(const char *name
,
199 const char **parent_names
, int num_parents
,
200 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
203 periph
->gate
.flags
|= TEGRA_PERIPH_NO_DIV
;
204 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
205 periph
, clk_base
, offset
, CLK_SET_RATE_PARENT
);