xref: /openbmc/linux/drivers/clk/tegra/clk-periph.c (revision 97da55fc)
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
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.
7  *
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
11  * more details.
12  *
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/>.
15  */
16 
17 #include <linux/clk.h>
18 #include <linux/clk-provider.h>
19 #include <linux/slab.h>
20 #include <linux/err.h>
21 
22 #include "clk.h"
23 
24 static u8 clk_periph_get_parent(struct clk_hw *hw)
25 {
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;
29 
30 	mux_hw->clk = hw->clk;
31 
32 	return mux_ops->get_parent(mux_hw);
33 }
34 
35 static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
36 {
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;
40 
41 	mux_hw->clk = hw->clk;
42 
43 	return mux_ops->set_parent(mux_hw, index);
44 }
45 
46 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
47 					    unsigned long parent_rate)
48 {
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;
52 
53 	div_hw->clk = hw->clk;
54 
55 	return div_ops->recalc_rate(div_hw, parent_rate);
56 }
57 
58 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
59 				  unsigned long *prate)
60 {
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;
64 
65 	div_hw->clk = hw->clk;
66 
67 	return div_ops->round_rate(div_hw, rate, prate);
68 }
69 
70 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
71 			       unsigned long parent_rate)
72 {
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;
76 
77 	div_hw->clk = hw->clk;
78 
79 	return div_ops->set_rate(div_hw, rate, parent_rate);
80 }
81 
82 static int clk_periph_is_enabled(struct clk_hw *hw)
83 {
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;
87 
88 	gate_hw->clk = hw->clk;
89 
90 	return gate_ops->is_enabled(gate_hw);
91 }
92 
93 static int clk_periph_enable(struct clk_hw *hw)
94 {
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;
98 
99 	gate_hw->clk = hw->clk;
100 
101 	return gate_ops->enable(gate_hw);
102 }
103 
104 static void clk_periph_disable(struct clk_hw *hw)
105 {
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;
109 
110 	gate_ops->disable(gate_hw);
111 }
112 
113 void tegra_periph_reset_deassert(struct clk *c)
114 {
115 	struct clk_hw *hw = __clk_get_hw(c);
116 	struct tegra_clk_periph *periph = to_clk_periph(hw);
117 	struct tegra_clk_periph_gate *gate;
118 
119 	if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
120 		gate = to_clk_periph_gate(hw);
121 		if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
122 			WARN_ON(1);
123 			return;
124 		}
125 	} else {
126 		gate = &periph->gate;
127 	}
128 
129 	tegra_periph_reset(gate, 0);
130 }
131 
132 void tegra_periph_reset_assert(struct clk *c)
133 {
134 	struct clk_hw *hw = __clk_get_hw(c);
135 	struct tegra_clk_periph *periph = to_clk_periph(hw);
136 	struct tegra_clk_periph_gate *gate;
137 
138 	if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
139 		gate = to_clk_periph_gate(hw);
140 		if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
141 			WARN_ON(1);
142 			return;
143 		}
144 	} else {
145 		gate = &periph->gate;
146 	}
147 
148 	tegra_periph_reset(gate, 1);
149 }
150 
151 const struct clk_ops tegra_clk_periph_ops = {
152 	.get_parent = clk_periph_get_parent,
153 	.set_parent = clk_periph_set_parent,
154 	.recalc_rate = clk_periph_recalc_rate,
155 	.round_rate = clk_periph_round_rate,
156 	.set_rate = clk_periph_set_rate,
157 	.is_enabled = clk_periph_is_enabled,
158 	.enable = clk_periph_enable,
159 	.disable = clk_periph_disable,
160 };
161 
162 const struct clk_ops tegra_clk_periph_nodiv_ops = {
163 	.get_parent = clk_periph_get_parent,
164 	.set_parent = clk_periph_set_parent,
165 	.is_enabled = clk_periph_is_enabled,
166 	.enable = clk_periph_enable,
167 	.disable = clk_periph_disable,
168 };
169 
170 static struct clk *_tegra_clk_register_periph(const char *name,
171 			const char **parent_names, int num_parents,
172 			struct tegra_clk_periph *periph,
173 			void __iomem *clk_base, u32 offset, bool div)
174 {
175 	struct clk *clk;
176 	struct clk_init_data init;
177 
178 	init.name = name;
179 	init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
180 	init.flags = div ? 0 : CLK_SET_RATE_PARENT;
181 	init.parent_names = parent_names;
182 	init.num_parents = num_parents;
183 
184 	/* Data in .init is copied by clk_register(), so stack variable OK */
185 	periph->hw.init = &init;
186 	periph->magic = TEGRA_CLK_PERIPH_MAGIC;
187 	periph->mux.reg = clk_base + offset;
188 	periph->divider.reg = div ? (clk_base + offset) : NULL;
189 	periph->gate.clk_base = clk_base;
190 
191 	clk = clk_register(NULL, &periph->hw);
192 	if (IS_ERR(clk))
193 		return clk;
194 
195 	periph->mux.hw.clk = clk;
196 	periph->divider.hw.clk = div ? clk : NULL;
197 	periph->gate.hw.clk = clk;
198 
199 	return clk;
200 }
201 
202 struct clk *tegra_clk_register_periph(const char *name,
203 		const char **parent_names, int num_parents,
204 		struct tegra_clk_periph *periph, void __iomem *clk_base,
205 		u32 offset)
206 {
207 	return _tegra_clk_register_periph(name, parent_names, num_parents,
208 			periph, clk_base, offset, true);
209 }
210 
211 struct clk *tegra_clk_register_periph_nodiv(const char *name,
212 		const char **parent_names, int num_parents,
213 		struct tegra_clk_periph *periph, void __iomem *clk_base,
214 		u32 offset)
215 {
216 	return _tegra_clk_register_periph(name, parent_names, num_parents,
217 			periph, clk_base, offset, false);
218 }
219