1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/clk.h>
7*4882a593Smuzhiyun #include <linux/clk-provider.h>
8*4882a593Smuzhiyun #include <linux/export.h>
9*4882a593Smuzhiyun #include <linux/slab.h>
10*4882a593Smuzhiyun #include <linux/err.h>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include "clk.h"
13*4882a593Smuzhiyun
clk_periph_get_parent(struct clk_hw * hw)14*4882a593Smuzhiyun static u8 clk_periph_get_parent(struct clk_hw *hw)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
17*4882a593Smuzhiyun const struct clk_ops *mux_ops = periph->mux_ops;
18*4882a593Smuzhiyun struct clk_hw *mux_hw = &periph->mux.hw;
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun __clk_hw_set_clk(mux_hw, hw);
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun return mux_ops->get_parent(mux_hw);
23*4882a593Smuzhiyun }
24*4882a593Smuzhiyun
clk_periph_set_parent(struct clk_hw * hw,u8 index)25*4882a593Smuzhiyun static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
28*4882a593Smuzhiyun const struct clk_ops *mux_ops = periph->mux_ops;
29*4882a593Smuzhiyun struct clk_hw *mux_hw = &periph->mux.hw;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun __clk_hw_set_clk(mux_hw, hw);
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun return mux_ops->set_parent(mux_hw, index);
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
clk_periph_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)36*4882a593Smuzhiyun static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
37*4882a593Smuzhiyun unsigned long parent_rate)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
40*4882a593Smuzhiyun const struct clk_ops *div_ops = periph->div_ops;
41*4882a593Smuzhiyun struct clk_hw *div_hw = &periph->divider.hw;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun __clk_hw_set_clk(div_hw, hw);
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun return div_ops->recalc_rate(div_hw, parent_rate);
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
clk_periph_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)48*4882a593Smuzhiyun static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
49*4882a593Smuzhiyun unsigned long *prate)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
52*4882a593Smuzhiyun const struct clk_ops *div_ops = periph->div_ops;
53*4882a593Smuzhiyun struct clk_hw *div_hw = &periph->divider.hw;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun __clk_hw_set_clk(div_hw, hw);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun return div_ops->round_rate(div_hw, rate, prate);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
clk_periph_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)60*4882a593Smuzhiyun static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
61*4882a593Smuzhiyun unsigned long parent_rate)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
64*4882a593Smuzhiyun const struct clk_ops *div_ops = periph->div_ops;
65*4882a593Smuzhiyun struct clk_hw *div_hw = &periph->divider.hw;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun __clk_hw_set_clk(div_hw, hw);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun return div_ops->set_rate(div_hw, rate, parent_rate);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
clk_periph_is_enabled(struct clk_hw * hw)72*4882a593Smuzhiyun static int clk_periph_is_enabled(struct clk_hw *hw)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
75*4882a593Smuzhiyun const struct clk_ops *gate_ops = periph->gate_ops;
76*4882a593Smuzhiyun struct clk_hw *gate_hw = &periph->gate.hw;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun __clk_hw_set_clk(gate_hw, hw);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun return gate_ops->is_enabled(gate_hw);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
clk_periph_enable(struct clk_hw * hw)83*4882a593Smuzhiyun static int clk_periph_enable(struct clk_hw *hw)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
86*4882a593Smuzhiyun const struct clk_ops *gate_ops = periph->gate_ops;
87*4882a593Smuzhiyun struct clk_hw *gate_hw = &periph->gate.hw;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun __clk_hw_set_clk(gate_hw, hw);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun return gate_ops->enable(gate_hw);
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
clk_periph_disable(struct clk_hw * hw)94*4882a593Smuzhiyun static void clk_periph_disable(struct clk_hw *hw)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
97*4882a593Smuzhiyun const struct clk_ops *gate_ops = periph->gate_ops;
98*4882a593Smuzhiyun struct clk_hw *gate_hw = &periph->gate.hw;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun gate_ops->disable(gate_hw);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
clk_periph_disable_unused(struct clk_hw * hw)103*4882a593Smuzhiyun static void clk_periph_disable_unused(struct clk_hw *hw)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
106*4882a593Smuzhiyun const struct clk_ops *gate_ops = periph->gate_ops;
107*4882a593Smuzhiyun struct clk_hw *gate_hw = &periph->gate.hw;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun gate_ops->disable_unused(gate_hw);
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun
clk_periph_restore_context(struct clk_hw * hw)112*4882a593Smuzhiyun static void clk_periph_restore_context(struct clk_hw *hw)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun struct tegra_clk_periph *periph = to_clk_periph(hw);
115*4882a593Smuzhiyun const struct clk_ops *div_ops = periph->div_ops;
116*4882a593Smuzhiyun struct clk_hw *div_hw = &periph->divider.hw;
117*4882a593Smuzhiyun int parent_id;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun parent_id = clk_hw_get_parent_index(hw);
120*4882a593Smuzhiyun if (WARN_ON(parent_id < 0))
121*4882a593Smuzhiyun return;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
124*4882a593Smuzhiyun div_ops->restore_context(div_hw);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun clk_periph_set_parent(hw, parent_id);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun const struct clk_ops tegra_clk_periph_ops = {
130*4882a593Smuzhiyun .get_parent = clk_periph_get_parent,
131*4882a593Smuzhiyun .set_parent = clk_periph_set_parent,
132*4882a593Smuzhiyun .recalc_rate = clk_periph_recalc_rate,
133*4882a593Smuzhiyun .round_rate = clk_periph_round_rate,
134*4882a593Smuzhiyun .set_rate = clk_periph_set_rate,
135*4882a593Smuzhiyun .is_enabled = clk_periph_is_enabled,
136*4882a593Smuzhiyun .enable = clk_periph_enable,
137*4882a593Smuzhiyun .disable = clk_periph_disable,
138*4882a593Smuzhiyun .disable_unused = clk_periph_disable_unused,
139*4882a593Smuzhiyun .restore_context = clk_periph_restore_context,
140*4882a593Smuzhiyun };
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun static const struct clk_ops tegra_clk_periph_nodiv_ops = {
143*4882a593Smuzhiyun .get_parent = clk_periph_get_parent,
144*4882a593Smuzhiyun .set_parent = clk_periph_set_parent,
145*4882a593Smuzhiyun .is_enabled = clk_periph_is_enabled,
146*4882a593Smuzhiyun .enable = clk_periph_enable,
147*4882a593Smuzhiyun .disable = clk_periph_disable,
148*4882a593Smuzhiyun .disable_unused = clk_periph_disable_unused,
149*4882a593Smuzhiyun .restore_context = clk_periph_restore_context,
150*4882a593Smuzhiyun };
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun static const struct clk_ops tegra_clk_periph_no_gate_ops = {
153*4882a593Smuzhiyun .get_parent = clk_periph_get_parent,
154*4882a593Smuzhiyun .set_parent = clk_periph_set_parent,
155*4882a593Smuzhiyun .recalc_rate = clk_periph_recalc_rate,
156*4882a593Smuzhiyun .round_rate = clk_periph_round_rate,
157*4882a593Smuzhiyun .set_rate = clk_periph_set_rate,
158*4882a593Smuzhiyun .restore_context = clk_periph_restore_context,
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun
_tegra_clk_register_periph(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset,unsigned long flags)161*4882a593Smuzhiyun static struct clk *_tegra_clk_register_periph(const char *name,
162*4882a593Smuzhiyun const char * const *parent_names, int num_parents,
163*4882a593Smuzhiyun struct tegra_clk_periph *periph,
164*4882a593Smuzhiyun void __iomem *clk_base, u32 offset,
165*4882a593Smuzhiyun unsigned long flags)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun struct clk *clk;
168*4882a593Smuzhiyun struct clk_init_data init;
169*4882a593Smuzhiyun const struct tegra_clk_periph_regs *bank;
170*4882a593Smuzhiyun bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
173*4882a593Smuzhiyun flags |= CLK_SET_RATE_PARENT;
174*4882a593Smuzhiyun init.ops = &tegra_clk_periph_nodiv_ops;
175*4882a593Smuzhiyun } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
176*4882a593Smuzhiyun init.ops = &tegra_clk_periph_no_gate_ops;
177*4882a593Smuzhiyun else
178*4882a593Smuzhiyun init.ops = &tegra_clk_periph_ops;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun init.name = name;
181*4882a593Smuzhiyun init.flags = flags;
182*4882a593Smuzhiyun init.parent_names = parent_names;
183*4882a593Smuzhiyun init.num_parents = num_parents;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun bank = get_reg_bank(periph->gate.clk_num);
186*4882a593Smuzhiyun if (!bank)
187*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* Data in .init is copied by clk_register(), so stack variable OK */
190*4882a593Smuzhiyun periph->hw.init = &init;
191*4882a593Smuzhiyun periph->magic = TEGRA_CLK_PERIPH_MAGIC;
192*4882a593Smuzhiyun periph->mux.reg = clk_base + offset;
193*4882a593Smuzhiyun periph->divider.reg = div ? (clk_base + offset) : NULL;
194*4882a593Smuzhiyun periph->gate.clk_base = clk_base;
195*4882a593Smuzhiyun periph->gate.regs = bank;
196*4882a593Smuzhiyun periph->gate.enable_refcnt = periph_clk_enb_refcnt;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun clk = clk_register(NULL, &periph->hw);
199*4882a593Smuzhiyun if (IS_ERR(clk))
200*4882a593Smuzhiyun return clk;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun periph->mux.hw.clk = clk;
203*4882a593Smuzhiyun periph->divider.hw.clk = div ? clk : NULL;
204*4882a593Smuzhiyun periph->gate.hw.clk = clk;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun return clk;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
tegra_clk_register_periph(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset,unsigned long flags)209*4882a593Smuzhiyun struct clk *tegra_clk_register_periph(const char *name,
210*4882a593Smuzhiyun const char * const *parent_names, int num_parents,
211*4882a593Smuzhiyun struct tegra_clk_periph *periph, void __iomem *clk_base,
212*4882a593Smuzhiyun u32 offset, unsigned long flags)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun return _tegra_clk_register_periph(name, parent_names, num_parents,
215*4882a593Smuzhiyun periph, clk_base, offset, flags);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
tegra_clk_register_periph_nodiv(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset)218*4882a593Smuzhiyun struct clk *tegra_clk_register_periph_nodiv(const char *name,
219*4882a593Smuzhiyun const char * const *parent_names, int num_parents,
220*4882a593Smuzhiyun struct tegra_clk_periph *periph, void __iomem *clk_base,
221*4882a593Smuzhiyun u32 offset)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
224*4882a593Smuzhiyun return _tegra_clk_register_periph(name, parent_names, num_parents,
225*4882a593Smuzhiyun periph, clk_base, offset, CLK_SET_RATE_PARENT);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
tegra_clk_register_periph_data(void __iomem * clk_base,struct tegra_periph_init_data * init)228*4882a593Smuzhiyun struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
229*4882a593Smuzhiyun struct tegra_periph_init_data *init)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun return _tegra_clk_register_periph(init->name, init->p.parent_names,
232*4882a593Smuzhiyun init->num_parents, &init->periph,
233*4882a593Smuzhiyun clk_base, init->offset, init->flags);
234*4882a593Smuzhiyun }
235