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-provider.h>
18 #include <linux/export.h>
19 #include <linux/slab.h>
20 #include <linux/err.h>
24 static u8
clk_periph_get_parent(struct clk_hw
*hw
)
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
;
30 __clk_hw_set_clk(mux_hw
, hw
);
32 return mux_ops
->get_parent(mux_hw
);
35 static int clk_periph_set_parent(struct clk_hw
*hw
, u8 index
)
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
;
41 __clk_hw_set_clk(mux_hw
, hw
);
43 return mux_ops
->set_parent(mux_hw
, index
);
46 static unsigned long clk_periph_recalc_rate(struct clk_hw
*hw
,
47 unsigned long parent_rate
)
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
;
53 __clk_hw_set_clk(div_hw
, hw
);
55 return div_ops
->recalc_rate(div_hw
, parent_rate
);
58 static long clk_periph_round_rate(struct clk_hw
*hw
, unsigned long rate
,
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
;
65 __clk_hw_set_clk(div_hw
, hw
);
67 return div_ops
->round_rate(div_hw
, rate
, prate
);
70 static int clk_periph_set_rate(struct clk_hw
*hw
, unsigned long rate
,
71 unsigned long parent_rate
)
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
;
77 __clk_hw_set_clk(div_hw
, hw
);
79 return div_ops
->set_rate(div_hw
, rate
, parent_rate
);
82 static int clk_periph_is_enabled(struct clk_hw
*hw
)
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
;
88 __clk_hw_set_clk(gate_hw
, hw
);
90 return gate_ops
->is_enabled(gate_hw
);
93 static int clk_periph_enable(struct clk_hw
*hw
)
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
;
99 __clk_hw_set_clk(gate_hw
, hw
);
101 return gate_ops
->enable(gate_hw
);
104 static void clk_periph_disable(struct clk_hw
*hw
)
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
;
110 gate_ops
->disable(gate_hw
);
113 const struct clk_ops tegra_clk_periph_ops
= {
114 .get_parent
= clk_periph_get_parent
,
115 .set_parent
= clk_periph_set_parent
,
116 .recalc_rate
= clk_periph_recalc_rate
,
117 .round_rate
= clk_periph_round_rate
,
118 .set_rate
= clk_periph_set_rate
,
119 .is_enabled
= clk_periph_is_enabled
,
120 .enable
= clk_periph_enable
,
121 .disable
= clk_periph_disable
,
124 static const struct clk_ops tegra_clk_periph_nodiv_ops
= {
125 .get_parent
= clk_periph_get_parent
,
126 .set_parent
= clk_periph_set_parent
,
127 .is_enabled
= clk_periph_is_enabled
,
128 .enable
= clk_periph_enable
,
129 .disable
= clk_periph_disable
,
132 static const struct clk_ops tegra_clk_periph_no_gate_ops
= {
133 .get_parent
= clk_periph_get_parent
,
134 .set_parent
= clk_periph_set_parent
,
135 .recalc_rate
= clk_periph_recalc_rate
,
136 .round_rate
= clk_periph_round_rate
,
137 .set_rate
= clk_periph_set_rate
,
140 static struct clk
*_tegra_clk_register_periph(const char *name
,
141 const char **parent_names
, int num_parents
,
142 struct tegra_clk_periph
*periph
,
143 void __iomem
*clk_base
, u32 offset
,
147 struct clk_init_data init
;
148 const struct tegra_clk_periph_regs
*bank
;
149 bool div
= !(periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
);
151 if (periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
) {
152 flags
|= CLK_SET_RATE_PARENT
;
153 init
.ops
= &tegra_clk_periph_nodiv_ops
;
154 } else if (periph
->gate
.flags
& TEGRA_PERIPH_NO_GATE
)
155 init
.ops
= &tegra_clk_periph_no_gate_ops
;
157 init
.ops
= &tegra_clk_periph_ops
;
161 init
.parent_names
= parent_names
;
162 init
.num_parents
= num_parents
;
164 bank
= get_reg_bank(periph
->gate
.clk_num
);
166 return ERR_PTR(-EINVAL
);
168 /* Data in .init is copied by clk_register(), so stack variable OK */
169 periph
->hw
.init
= &init
;
170 periph
->magic
= TEGRA_CLK_PERIPH_MAGIC
;
171 periph
->mux
.reg
= clk_base
+ offset
;
172 periph
->divider
.reg
= div
? (clk_base
+ offset
) : NULL
;
173 periph
->gate
.clk_base
= clk_base
;
174 periph
->gate
.regs
= bank
;
175 periph
->gate
.enable_refcnt
= periph_clk_enb_refcnt
;
177 clk
= clk_register(NULL
, &periph
->hw
);
181 periph
->mux
.hw
.clk
= clk
;
182 periph
->divider
.hw
.clk
= div
? clk
: NULL
;
183 periph
->gate
.hw
.clk
= clk
;
188 struct clk
*tegra_clk_register_periph(const char *name
,
189 const char **parent_names
, int num_parents
,
190 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
191 u32 offset
, unsigned long flags
)
193 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
194 periph
, clk_base
, offset
, flags
);
197 struct clk
*tegra_clk_register_periph_nodiv(const char *name
,
198 const char **parent_names
, int num_parents
,
199 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
202 periph
->gate
.flags
|= TEGRA_PERIPH_NO_DIV
;
203 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
204 periph
, clk_base
, offset
, CLK_SET_RATE_PARENT
);