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_round_rate(struct clk_hw *hw, unsigned long rate,
59 : : unsigned long *prate)
60 : : {
61 : : struct clk_composite *composite = to_clk_composite(hw);
62 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
63 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
64 : :
65 : 0 : rate_hw->clk = hw->clk;
66 : :
67 : 0 : return rate_ops->round_rate(rate_hw, rate, prate);
68 : : }
69 : :
70 : 0 : static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
71 : : unsigned long parent_rate)
72 : : {
73 : : struct clk_composite *composite = to_clk_composite(hw);
74 : 0 : const struct clk_ops *rate_ops = composite->rate_ops;
75 : 0 : struct clk_hw *rate_hw = composite->rate_hw;
76 : :
77 : 0 : rate_hw->clk = hw->clk;
78 : :
79 : 0 : return rate_ops->set_rate(rate_hw, rate, parent_rate);
80 : : }
81 : :
82 : 0 : static int clk_composite_is_enabled(struct clk_hw *hw)
83 : : {
84 : : struct clk_composite *composite = to_clk_composite(hw);
85 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
86 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
87 : :
88 : 0 : gate_hw->clk = hw->clk;
89 : :
90 : 0 : return gate_ops->is_enabled(gate_hw);
91 : : }
92 : :
93 : 0 : static int clk_composite_enable(struct clk_hw *hw)
94 : : {
95 : : struct clk_composite *composite = to_clk_composite(hw);
96 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
97 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
98 : :
99 : 0 : gate_hw->clk = hw->clk;
100 : :
101 : 0 : return gate_ops->enable(gate_hw);
102 : : }
103 : :
104 : 0 : static void clk_composite_disable(struct clk_hw *hw)
105 : : {
106 : : struct clk_composite *composite = to_clk_composite(hw);
107 : 0 : const struct clk_ops *gate_ops = composite->gate_ops;
108 : 0 : struct clk_hw *gate_hw = composite->gate_hw;
109 : :
110 : 0 : gate_hw->clk = hw->clk;
111 : :
112 : 0 : gate_ops->disable(gate_hw);
113 : 0 : }
114 : :
115 : 0 : struct clk *clk_register_composite(struct device *dev, const char *name,
116 : : const char **parent_names, int num_parents,
117 : : struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
118 : : struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
119 : : struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
120 : : unsigned long flags)
121 : : {
122 : : struct clk *clk;
123 : : struct clk_init_data init;
124 : : struct clk_composite *composite;
125 : : struct clk_ops *clk_composite_ops;
126 : :
127 : : composite = kzalloc(sizeof(*composite), GFP_KERNEL);
128 [ # # ]: 0 : if (!composite) {
129 : 0 : pr_err("%s: could not allocate composite clk\n", __func__);
130 : 0 : return ERR_PTR(-ENOMEM);
131 : : }
132 : :
133 : 0 : init.name = name;
134 : 0 : init.flags = flags | CLK_IS_BASIC;
135 : 0 : init.parent_names = parent_names;
136 : 0 : init.num_parents = num_parents;
137 : :
138 : 0 : clk_composite_ops = &composite->ops;
139 : :
140 [ # # ]: 0 : if (mux_hw && mux_ops) {
141 [ # # ][ # # ]: 0 : if (!mux_ops->get_parent || !mux_ops->set_parent) {
142 : : clk = ERR_PTR(-EINVAL);
143 : : goto err;
144 : : }
145 : :
146 : 0 : composite->mux_hw = mux_hw;
147 : 0 : composite->mux_ops = mux_ops;
148 : 0 : clk_composite_ops->get_parent = clk_composite_get_parent;
149 : 0 : clk_composite_ops->set_parent = clk_composite_set_parent;
150 : : }
151 : :
152 [ # # ]: 0 : if (rate_hw && rate_ops) {
153 [ # # ]: 0 : if (!rate_ops->recalc_rate) {
154 : : clk = ERR_PTR(-EINVAL);
155 : : goto err;
156 : : }
157 : :
158 : : /* .round_rate is a prerequisite for .set_rate */
159 [ # # ]: 0 : if (rate_ops->round_rate) {
160 : 0 : clk_composite_ops->round_rate = clk_composite_round_rate;
161 [ # # ]: 0 : if (rate_ops->set_rate) {
162 : 0 : clk_composite_ops->set_rate = clk_composite_set_rate;
163 : : }
164 : : } else {
165 [ # # ]: 0 : WARN(rate_ops->set_rate,
166 : : "%s: missing round_rate op is required\n",
167 : : __func__);
168 : : }
169 : :
170 : 0 : composite->rate_hw = rate_hw;
171 : 0 : composite->rate_ops = rate_ops;
172 : 0 : clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
173 : : }
174 : :
175 [ # # ]: 0 : if (gate_hw && gate_ops) {
176 [ # # ][ # # ]: 0 : if (!gate_ops->is_enabled || !gate_ops->enable ||
[ # # ]
177 : 0 : !gate_ops->disable) {
178 : : clk = ERR_PTR(-EINVAL);
179 : : goto err;
180 : : }
181 : :
182 : 0 : composite->gate_hw = gate_hw;
183 : 0 : composite->gate_ops = gate_ops;
184 : 0 : clk_composite_ops->is_enabled = clk_composite_is_enabled;
185 : 0 : clk_composite_ops->enable = clk_composite_enable;
186 : 0 : clk_composite_ops->disable = clk_composite_disable;
187 : : }
188 : :
189 : 0 : init.ops = clk_composite_ops;
190 : 0 : composite->hw.init = &init;
191 : :
192 : 0 : clk = clk_register(dev, &composite->hw);
193 [ # # ]: 0 : if (IS_ERR(clk))
194 : : goto err;
195 : :
196 [ # # ]: 0 : if (composite->mux_hw)
197 : 0 : composite->mux_hw->clk = clk;
198 : :
199 [ # # ]: 0 : if (composite->rate_hw)
200 : 0 : composite->rate_hw->clk = clk;
201 : :
202 [ # # ]: 0 : if (composite->gate_hw)
203 : 0 : composite->gate_hw->clk = clk;
204 : :
205 : 0 : return clk;
206 : :
207 : : err:
208 : 0 : kfree(composite);
209 : 0 : return clk;
210 : : }
|