Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2013 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/err.h>
20 : : #include <linux/slab.h>
21 : :
22 : : #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
23 : :
24 : 0 : static u8 clk_composite_get_parent(struct clk_hw *hw)
25 : : {
26 : : struct clk_composite *composite = to_clk_composite(hw);
27 : 0 : const struct clk_ops *mux_ops = composite->mux_ops;
28 : 0 : struct clk_hw *mux_hw = composite->mux_hw;
29 : :
30 : 0 : mux_hw->clk = hw->clk;
31 : :
32 : 0 : return mux_ops->get_parent(mux_hw);
33 : : }
34 : :
35 : 0 : static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
36 : : {
37 : : struct clk_composite *composite = to_clk_composite(hw);
38 : 0 : const struct clk_ops *mux_ops = composite->mux_ops;
39 : 0 : struct clk_hw *mux_hw = composite->mux_hw;
40 : :
41 : 0 : mux_hw->clk = hw->clk;
42 : :
43 : 0 : return mux_ops->set_parent(mux_hw, index);
44 : : }
45 : :
46 : 0 : static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
47 : : unsigned long parent_rate)
48 : : {
49 : : struct clk_composite *composite = to_clk_composite(hw);
50 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
51 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
52 : :
53 : 0 : rate_hw->clk = hw->clk;
54 : :
55 : 0 : return rate_ops->recalc_rate(rate_hw, parent_rate);
56 : : }
57 : :
58 : 0 : static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
59 : : unsigned long *best_parent_rate,
60 : : struct clk **best_parent_p)
61 : : {
62 : : struct clk_composite *composite = to_clk_composite(hw);
63 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
64 : 0 : const struct clk_ops *mux_ops = composite->mux_ops;
65 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
66 : 0 : struct clk_hw *mux_hw = composite->mux_hw;
67 : :
68 [ # # ][ # # ]: 0 : if (rate_hw && rate_ops && rate_ops->determine_rate) {
69 : 0 : rate_hw->clk = hw->clk;
70 : 0 : return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
71 : : best_parent_p);
72 [ # # ][ # # ]: 0 : } else if (mux_hw && mux_ops && mux_ops->determine_rate) {
73 : 0 : mux_hw->clk = hw->clk;
74 : 0 : return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
75 : : best_parent_p);
76 : : } else {
77 : 0 : pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n");
78 : 0 : return 0;
79 : : }
80 : : }
81 : :
82 : 0 : static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
83 : : unsigned long *prate)
84 : : {
85 : : struct clk_composite *composite = to_clk_composite(hw);
86 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
87 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
88 : :
89 : 0 : rate_hw->clk = hw->clk;
90 : :
91 : 0 : return rate_ops->round_rate(rate_hw, rate, prate);
92 : : }
93 : :
94 : 0 : static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
95 : : unsigned long parent_rate)
96 : : {
97 : : struct clk_composite *composite = to_clk_composite(hw);
98 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
99 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
100 : :
101 : 0 : rate_hw->clk = hw->clk;
102 : :
103 : 0 : return rate_ops->set_rate(rate_hw, rate, parent_rate);
104 : : }
105 : :
106 : 0 : static int clk_composite_is_enabled(struct clk_hw *hw)
107 : : {
108 : : struct clk_composite *composite = to_clk_composite(hw);
109 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
110 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
111 : :
112 : 0 : gate_hw->clk = hw->clk;
113 : :
114 : 0 : return gate_ops->is_enabled(gate_hw);
115 : : }
116 : :
117 : 0 : static int clk_composite_enable(struct clk_hw *hw)
118 : : {
119 : : struct clk_composite *composite = to_clk_composite(hw);
120 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
121 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
122 : :
123 : 0 : gate_hw->clk = hw->clk;
124 : :
125 : 0 : return gate_ops->enable(gate_hw);
126 : : }
127 : :
128 : 0 : static void clk_composite_disable(struct clk_hw *hw)
129 : : {
130 : : struct clk_composite *composite = to_clk_composite(hw);
131 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
132 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
133 : :
134 : 0 : gate_hw->clk = hw->clk;
135 : :
136 : 0 : gate_ops->disable(gate_hw);
137 : 0 : }
138 : :
139 : 0 : struct clk *clk_register_composite(struct device *dev, const char *name,
140 : : const char **parent_names, int num_parents,
141 : : struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
142 : : struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
143 : : struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
144 : : unsigned long flags)
145 : : {
146 : : struct clk *clk;
147 : : struct clk_init_data init;
148 : : struct clk_composite *composite;
149 : : struct clk_ops *clk_composite_ops;
150 : :
151 : : composite = kzalloc(sizeof(*composite), GFP_KERNEL);
152 [ # # ]: 0 : if (!composite) {
153 : 0 : pr_err("%s: could not allocate composite clk\n", __func__);
154 : 0 : return ERR_PTR(-ENOMEM);
155 : : }
156 : :
157 : 0 : init.name = name;
158 : 0 : init.flags = flags | CLK_IS_BASIC;
159 : 0 : init.parent_names = parent_names;
160 : 0 : init.num_parents = num_parents;
161 : :
162 : 0 : clk_composite_ops = &composite->ops;
163 : :
164 [ # # ]: 0 : if (mux_hw && mux_ops) {
165 [ # # ][ # # ]: 0 : if (!mux_ops->get_parent || !mux_ops->set_parent) {
166 : : clk = ERR_PTR(-EINVAL);
167 : : goto err;
168 : : }
169 : :
170 : 0 : composite->mux_hw = mux_hw;
171 : 0 : composite->mux_ops = mux_ops;
172 : 0 : clk_composite_ops->get_parent = clk_composite_get_parent;
173 : 0 : clk_composite_ops->set_parent = clk_composite_set_parent;
174 [ # # ]: 0 : if (mux_ops->determine_rate)
175 : 0 : clk_composite_ops->determine_rate = clk_composite_determine_rate;
176 : : }
177 : :
178 [ # # ]: 0 : if (rate_hw && rate_ops) {
179 [ # # ]: 0 : if (!rate_ops->recalc_rate) {
180 : : clk = ERR_PTR(-EINVAL);
181 : : goto err;
182 : : }
183 : :
184 : : /* .round_rate is a prerequisite for .set_rate */
185 [ # # ]: 0 : if (rate_ops->round_rate) {
186 : 0 : clk_composite_ops->round_rate = clk_composite_round_rate;
187 [ # # ]: 0 : if (rate_ops->set_rate) {
188 : 0 : clk_composite_ops->set_rate = clk_composite_set_rate;
189 : : }
190 : : } else {
191 [ # # ]: 0 : WARN(rate_ops->set_rate,
192 : : "%s: missing round_rate op is required\n",
193 : : __func__);
194 : : }
195 : :
196 : 0 : composite->rate_hw = rate_hw;
197 : 0 : composite->rate_ops = rate_ops;
198 : 0 : clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
199 [ # # ]: 0 : if (rate_ops->determine_rate)
200 : 0 : clk_composite_ops->determine_rate = clk_composite_determine_rate;
201 : : }
202 : :
203 [ # # ]: 0 : if (gate_hw && gate_ops) {
204 [ # # ][ # # ]: 0 : if (!gate_ops->is_enabled || !gate_ops->enable ||
[ # # ]
205 : 0 : !gate_ops->disable) {
206 : : clk = ERR_PTR(-EINVAL);
207 : : goto err;
208 : : }
209 : :
210 : 0 : composite->gate_hw = gate_hw;
211 : 0 : composite->gate_ops = gate_ops;
212 : 0 : clk_composite_ops->is_enabled = clk_composite_is_enabled;
213 : 0 : clk_composite_ops->enable = clk_composite_enable;
214 : 0 : clk_composite_ops->disable = clk_composite_disable;
215 : : }
216 : :
217 : 0 : init.ops = clk_composite_ops;
218 : 0 : composite->hw.init = &init;
219 : :
220 : 0 : clk = clk_register(dev, &composite->hw);
221 [ # # ]: 0 : if (IS_ERR(clk))
222 : : goto err;
223 : :
224 [ # # ]: 0 : if (composite->mux_hw)
225 : 0 : composite->mux_hw->clk = clk;
226 : :
227 [ # # ]: 0 : if (composite->rate_hw)
228 : 0 : composite->rate_hw->clk = clk;
229 : :
230 [ # # ]: 0 : if (composite->gate_hw)
231 : 0 : composite->gate_hw->clk = clk;
232 : :
233 : 0 : return clk;
234 : :
235 : : err:
236 : 0 : kfree(composite);
237 : 0 : return clk;
238 : : }
|