Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 24static u8 clk_composite_get_parent(struct clk_hw *hw)
 25{
 26	struct clk_composite *composite = to_clk_composite(hw);
 27	const struct clk_ops *mux_ops = composite->mux_ops;
 28	struct clk_hw *mux_hw = composite->mux_hw;
 29
 30	mux_hw->clk = hw->clk;
 31
 32	return mux_ops->get_parent(mux_hw);
 33}
 34
 35static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
 36{
 37	struct clk_composite *composite = to_clk_composite(hw);
 38	const struct clk_ops *mux_ops = composite->mux_ops;
 39	struct clk_hw *mux_hw = composite->mux_hw;
 40
 41	mux_hw->clk = hw->clk;
 42
 43	return mux_ops->set_parent(mux_hw, index);
 44}
 45
 46static 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	const struct clk_ops *rate_ops = composite->rate_ops;
 51	struct clk_hw *rate_hw = composite->rate_hw;
 52
 53	rate_hw->clk = hw->clk;
 54
 55	return rate_ops->recalc_rate(rate_hw, parent_rate);
 56}
 57
 58static 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	const struct clk_ops *rate_ops = composite->rate_ops;
 64	const struct clk_ops *mux_ops = composite->mux_ops;
 65	struct clk_hw *rate_hw = composite->rate_hw;
 66	struct clk_hw *mux_hw = composite->mux_hw;
 67
 68	if (rate_hw && rate_ops && rate_ops->determine_rate) {
 69		rate_hw->clk = hw->clk;
 70		return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
 71						best_parent_p);
 72	} else if (mux_hw && mux_ops && mux_ops->determine_rate) {
 73		mux_hw->clk = hw->clk;
 74		return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
 75					       best_parent_p);
 76	} else {
 77		pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n");
 78		return 0;
 79	}
 80}
 81
 82static 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	const struct clk_ops *rate_ops = composite->rate_ops;
 87	struct clk_hw *rate_hw = composite->rate_hw;
 88
 89	rate_hw->clk = hw->clk;
 90
 91	return rate_ops->round_rate(rate_hw, rate, prate);
 92}
 93
 94static 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	const struct clk_ops *rate_ops = composite->rate_ops;
 99	struct clk_hw *rate_hw = composite->rate_hw;
100
101	rate_hw->clk = hw->clk;
102
103	return rate_ops->set_rate(rate_hw, rate, parent_rate);
104}
105
106static int clk_composite_is_enabled(struct clk_hw *hw)
107{
108	struct clk_composite *composite = to_clk_composite(hw);
109	const struct clk_ops *gate_ops = composite->gate_ops;
110	struct clk_hw *gate_hw = composite->gate_hw;
111
112	gate_hw->clk = hw->clk;
113
114	return gate_ops->is_enabled(gate_hw);
115}
116
117static int clk_composite_enable(struct clk_hw *hw)
118{
119	struct clk_composite *composite = to_clk_composite(hw);
120	const struct clk_ops *gate_ops = composite->gate_ops;
121	struct clk_hw *gate_hw = composite->gate_hw;
122
123	gate_hw->clk = hw->clk;
124
125	return gate_ops->enable(gate_hw);
126}
127
128static void clk_composite_disable(struct clk_hw *hw)
129{
130	struct clk_composite *composite = to_clk_composite(hw);
131	const struct clk_ops *gate_ops = composite->gate_ops;
132	struct clk_hw *gate_hw = composite->gate_hw;
133
134	gate_hw->clk = hw->clk;
135
136	gate_ops->disable(gate_hw);
137}
138
139struct 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	if (!composite) {
153		pr_err("%s: could not allocate composite clk\n", __func__);
154		return ERR_PTR(-ENOMEM);
155	}
156
157	init.name = name;
158	init.flags = flags | CLK_IS_BASIC;
159	init.parent_names = parent_names;
160	init.num_parents = num_parents;
161
162	clk_composite_ops = &composite->ops;
163
164	if (mux_hw && mux_ops) {
165		if (!mux_ops->get_parent || !mux_ops->set_parent) {
166			clk = ERR_PTR(-EINVAL);
167			goto err;
168		}
169
170		composite->mux_hw = mux_hw;
171		composite->mux_ops = mux_ops;
172		clk_composite_ops->get_parent = clk_composite_get_parent;
173		clk_composite_ops->set_parent = clk_composite_set_parent;
174		if (mux_ops->determine_rate)
175			clk_composite_ops->determine_rate = clk_composite_determine_rate;
176	}
177
178	if (rate_hw && rate_ops) {
179		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		if (rate_ops->round_rate) {
186			clk_composite_ops->round_rate = clk_composite_round_rate;
187			if (rate_ops->set_rate) {
188				clk_composite_ops->set_rate = clk_composite_set_rate;
189			}
190		} else {
191			WARN(rate_ops->set_rate,
192				"%s: missing round_rate op is required\n",
193				__func__);
194		}
195
196		composite->rate_hw = rate_hw;
197		composite->rate_ops = rate_ops;
198		clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
199		if (rate_ops->determine_rate)
200			clk_composite_ops->determine_rate = clk_composite_determine_rate;
201	}
202
203	if (gate_hw && gate_ops) {
204		if (!gate_ops->is_enabled || !gate_ops->enable ||
205		    !gate_ops->disable) {
206			clk = ERR_PTR(-EINVAL);
207			goto err;
208		}
209
210		composite->gate_hw = gate_hw;
211		composite->gate_ops = gate_ops;
212		clk_composite_ops->is_enabled = clk_composite_is_enabled;
213		clk_composite_ops->enable = clk_composite_enable;
214		clk_composite_ops->disable = clk_composite_disable;
215	}
216
217	init.ops = clk_composite_ops;
218	composite->hw.init = &init;
219
220	clk = clk_register(dev, &composite->hw);
221	if (IS_ERR(clk))
222		goto err;
223
224	if (composite->mux_hw)
225		composite->mux_hw->clk = clk;
226
227	if (composite->rate_hw)
228		composite->rate_hw->clk = clk;
229
230	if (composite->gate_hw)
231		composite->gate_hw->clk = clk;
232
233	return clk;
234
235err:
236	kfree(composite);
237	return clk;
238}