1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
7 #include <linux/clk-provider.h>
8 #include <linux/export.h>
9 #include <linux/slab.h>
10 #include <linux/err.h>
14 static u8
clk_periph_get_parent(struct clk_hw
*hw
)
16 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
17 const struct clk_ops
*mux_ops
= periph
->mux_ops
;
18 struct clk_hw
*mux_hw
= &periph
->mux
.hw
;
20 __clk_hw_set_clk(mux_hw
, hw
);
22 return mux_ops
->get_parent(mux_hw
);
25 static int clk_periph_set_parent(struct clk_hw
*hw
, u8 index
)
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 __clk_hw_set_clk(mux_hw
, hw
);
33 return mux_ops
->set_parent(mux_hw
, index
);
36 static unsigned long clk_periph_recalc_rate(struct clk_hw
*hw
,
37 unsigned long parent_rate
)
39 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
40 const struct clk_ops
*div_ops
= periph
->div_ops
;
41 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
43 __clk_hw_set_clk(div_hw
, hw
);
45 return div_ops
->recalc_rate(div_hw
, parent_rate
);
48 static long clk_periph_round_rate(struct clk_hw
*hw
, unsigned long rate
,
51 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
52 const struct clk_ops
*div_ops
= periph
->div_ops
;
53 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
55 __clk_hw_set_clk(div_hw
, hw
);
57 return div_ops
->round_rate(div_hw
, rate
, prate
);
60 static int clk_periph_set_rate(struct clk_hw
*hw
, unsigned long rate
,
61 unsigned long parent_rate
)
63 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
64 const struct clk_ops
*div_ops
= periph
->div_ops
;
65 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
67 __clk_hw_set_clk(div_hw
, hw
);
69 return div_ops
->set_rate(div_hw
, rate
, parent_rate
);
72 static int clk_periph_is_enabled(struct clk_hw
*hw
)
74 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
75 const struct clk_ops
*gate_ops
= periph
->gate_ops
;
76 struct clk_hw
*gate_hw
= &periph
->gate
.hw
;
78 __clk_hw_set_clk(gate_hw
, hw
);
80 return gate_ops
->is_enabled(gate_hw
);
83 static int clk_periph_enable(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 __clk_hw_set_clk(gate_hw
, hw
);
91 return gate_ops
->enable(gate_hw
);
94 static void clk_periph_disable(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_ops
->disable(gate_hw
);
103 static void clk_periph_restore_context(struct clk_hw
*hw
)
105 struct tegra_clk_periph
*periph
= to_clk_periph(hw
);
106 const struct clk_ops
*div_ops
= periph
->div_ops
;
107 struct clk_hw
*div_hw
= &periph
->divider
.hw
;
110 parent_id
= clk_hw_get_parent_index(hw
);
111 if (WARN_ON(parent_id
< 0))
114 if (!(periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
))
115 div_ops
->restore_context(div_hw
);
117 clk_periph_set_parent(hw
, parent_id
);
120 const struct clk_ops tegra_clk_periph_ops
= {
121 .get_parent
= clk_periph_get_parent
,
122 .set_parent
= clk_periph_set_parent
,
123 .recalc_rate
= clk_periph_recalc_rate
,
124 .round_rate
= clk_periph_round_rate
,
125 .set_rate
= clk_periph_set_rate
,
126 .is_enabled
= clk_periph_is_enabled
,
127 .enable
= clk_periph_enable
,
128 .disable
= clk_periph_disable
,
129 .restore_context
= clk_periph_restore_context
,
132 static const struct clk_ops tegra_clk_periph_nodiv_ops
= {
133 .get_parent
= clk_periph_get_parent
,
134 .set_parent
= clk_periph_set_parent
,
135 .is_enabled
= clk_periph_is_enabled
,
136 .enable
= clk_periph_enable
,
137 .disable
= clk_periph_disable
,
138 .restore_context
= clk_periph_restore_context
,
141 static const struct clk_ops tegra_clk_periph_no_gate_ops
= {
142 .get_parent
= clk_periph_get_parent
,
143 .set_parent
= clk_periph_set_parent
,
144 .recalc_rate
= clk_periph_recalc_rate
,
145 .round_rate
= clk_periph_round_rate
,
146 .set_rate
= clk_periph_set_rate
,
147 .restore_context
= clk_periph_restore_context
,
150 static struct clk
*_tegra_clk_register_periph(const char *name
,
151 const char * const *parent_names
, int num_parents
,
152 struct tegra_clk_periph
*periph
,
153 void __iomem
*clk_base
, u32 offset
,
157 struct clk_init_data init
;
158 const struct tegra_clk_periph_regs
*bank
;
159 bool div
= !(periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
);
161 if (periph
->gate
.flags
& TEGRA_PERIPH_NO_DIV
) {
162 flags
|= CLK_SET_RATE_PARENT
;
163 init
.ops
= &tegra_clk_periph_nodiv_ops
;
164 } else if (periph
->gate
.flags
& TEGRA_PERIPH_NO_GATE
)
165 init
.ops
= &tegra_clk_periph_no_gate_ops
;
167 init
.ops
= &tegra_clk_periph_ops
;
171 init
.parent_names
= parent_names
;
172 init
.num_parents
= num_parents
;
174 bank
= get_reg_bank(periph
->gate
.clk_num
);
176 return ERR_PTR(-EINVAL
);
178 /* Data in .init is copied by clk_register(), so stack variable OK */
179 periph
->hw
.init
= &init
;
180 periph
->magic
= TEGRA_CLK_PERIPH_MAGIC
;
181 periph
->mux
.reg
= clk_base
+ offset
;
182 periph
->divider
.reg
= div
? (clk_base
+ offset
) : NULL
;
183 periph
->gate
.clk_base
= clk_base
;
184 periph
->gate
.regs
= bank
;
185 periph
->gate
.enable_refcnt
= periph_clk_enb_refcnt
;
187 clk
= clk_register(NULL
, &periph
->hw
);
191 periph
->mux
.hw
.clk
= clk
;
192 periph
->divider
.hw
.clk
= div
? clk
: NULL
;
193 periph
->gate
.hw
.clk
= clk
;
198 struct clk
*tegra_clk_register_periph(const char *name
,
199 const char * const *parent_names
, int num_parents
,
200 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
201 u32 offset
, unsigned long flags
)
203 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
204 periph
, clk_base
, offset
, flags
);
207 struct clk
*tegra_clk_register_periph_nodiv(const char *name
,
208 const char * const *parent_names
, int num_parents
,
209 struct tegra_clk_periph
*periph
, void __iomem
*clk_base
,
212 periph
->gate
.flags
|= TEGRA_PERIPH_NO_DIV
;
213 return _tegra_clk_register_periph(name
, parent_names
, num_parents
,
214 periph
, clk_base
, offset
, CLK_SET_RATE_PARENT
);
217 struct clk
*tegra_clk_register_periph_data(void __iomem
*clk_base
,
218 struct tegra_periph_init_data
*init
)
220 return _tegra_clk_register_periph(init
->name
, init
->p
.parent_names
,
221 init
->num_parents
, &init
->periph
,
222 clk_base
, init
->offset
, init
->flags
);