Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * PRCMU clock implementation for ux500 platform.
  4 *
  5 * Copyright (C) 2012 ST-Ericsson SA
  6 * Author: Ulf Hansson <ulf.hansson@linaro.org>
  7 */
  8
  9#include <linux/clk-provider.h>
 10#include <linux/mfd/dbx500-prcmu.h>
 11#include <linux/slab.h>
 12#include <linux/io.h>
 13#include <linux/err.h>
 14#include "clk.h"
 15
 16#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
 17#define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw)
 18
 19struct clk_prcmu {
 20	struct clk_hw hw;
 21	u8 cg_sel;
 22	int opp_requested;
 23};
 24
 25struct clk_prcmu_clkout {
 26	struct clk_hw hw;
 27	u8 clkout_id;
 28	u8 source;
 29	u8 divider;
 30};
 31
 32/* PRCMU clock operations. */
 33
 34static int clk_prcmu_prepare(struct clk_hw *hw)
 35{
 36	struct clk_prcmu *clk = to_clk_prcmu(hw);
 37
 38	return prcmu_request_clock(clk->cg_sel, true);
 39}
 40
 41static void clk_prcmu_unprepare(struct clk_hw *hw)
 42{
 43	struct clk_prcmu *clk = to_clk_prcmu(hw);
 44	if (prcmu_request_clock(clk->cg_sel, false))
 45		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
 46		       clk_hw_get_name(hw));
 47}
 48
 49static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
 50					   unsigned long parent_rate)
 51{
 52	struct clk_prcmu *clk = to_clk_prcmu(hw);
 53	return prcmu_clock_rate(clk->cg_sel);
 54}
 55
 56static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
 57				 unsigned long *parent_rate)
 58{
 59	struct clk_prcmu *clk = to_clk_prcmu(hw);
 60	return prcmu_round_clock_rate(clk->cg_sel, rate);
 61}
 62
 63static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
 64			      unsigned long parent_rate)
 65{
 66	struct clk_prcmu *clk = to_clk_prcmu(hw);
 67	return prcmu_set_clock_rate(clk->cg_sel, rate);
 68}
 69
 70static int clk_prcmu_opp_prepare(struct clk_hw *hw)
 71{
 72	int err;
 73	struct clk_prcmu *clk = to_clk_prcmu(hw);
 74
 75	if (!clk->opp_requested) {
 76		err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
 77						(char *)clk_hw_get_name(hw),
 78						100);
 79		if (err) {
 80			pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
 81				__func__, clk_hw_get_name(hw));
 82			return err;
 83		}
 84		clk->opp_requested = 1;
 85	}
 86
 87	err = prcmu_request_clock(clk->cg_sel, true);
 88	if (err) {
 89		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
 90					(char *)clk_hw_get_name(hw));
 91		clk->opp_requested = 0;
 92		return err;
 93	}
 94
 95	return 0;
 96}
 97
 98static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
 99{
100	struct clk_prcmu *clk = to_clk_prcmu(hw);
101
102	if (prcmu_request_clock(clk->cg_sel, false)) {
103		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
104			clk_hw_get_name(hw));
105		return;
106	}
107
108	if (clk->opp_requested) {
109		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
110					(char *)clk_hw_get_name(hw));
111		clk->opp_requested = 0;
112	}
113}
114
115static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
116{
117	int err;
118	struct clk_prcmu *clk = to_clk_prcmu(hw);
119
120	if (!clk->opp_requested) {
121		err = prcmu_request_ape_opp_100_voltage(true);
122		if (err) {
123			pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
124				__func__, clk_hw_get_name(hw));
125			return err;
126		}
127		clk->opp_requested = 1;
128	}
129
130	err = prcmu_request_clock(clk->cg_sel, true);
131	if (err) {
132		prcmu_request_ape_opp_100_voltage(false);
133		clk->opp_requested = 0;
134		return err;
135	}
136
137	return 0;
138}
139
140static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
141{
142	struct clk_prcmu *clk = to_clk_prcmu(hw);
143
144	if (prcmu_request_clock(clk->cg_sel, false)) {
145		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
146			clk_hw_get_name(hw));
147		return;
148	}
149
150	if (clk->opp_requested) {
151		prcmu_request_ape_opp_100_voltage(false);
152		clk->opp_requested = 0;
153	}
154}
155
156static const struct clk_ops clk_prcmu_scalable_ops = {
157	.prepare = clk_prcmu_prepare,
158	.unprepare = clk_prcmu_unprepare,
159	.recalc_rate = clk_prcmu_recalc_rate,
160	.round_rate = clk_prcmu_round_rate,
161	.set_rate = clk_prcmu_set_rate,
162};
163
164static const struct clk_ops clk_prcmu_gate_ops = {
165	.prepare = clk_prcmu_prepare,
166	.unprepare = clk_prcmu_unprepare,
167	.recalc_rate = clk_prcmu_recalc_rate,
168};
169
170static const struct clk_ops clk_prcmu_scalable_rate_ops = {
171	.recalc_rate = clk_prcmu_recalc_rate,
172	.round_rate = clk_prcmu_round_rate,
173	.set_rate = clk_prcmu_set_rate,
174};
175
176static const struct clk_ops clk_prcmu_rate_ops = {
177	.recalc_rate = clk_prcmu_recalc_rate,
178};
179
180static const struct clk_ops clk_prcmu_opp_gate_ops = {
181	.prepare = clk_prcmu_opp_prepare,
182	.unprepare = clk_prcmu_opp_unprepare,
183	.recalc_rate = clk_prcmu_recalc_rate,
184};
185
186static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
187	.prepare = clk_prcmu_opp_volt_prepare,
188	.unprepare = clk_prcmu_opp_volt_unprepare,
189	.recalc_rate = clk_prcmu_recalc_rate,
190	.round_rate = clk_prcmu_round_rate,
191	.set_rate = clk_prcmu_set_rate,
192};
193
194static struct clk_hw *clk_reg_prcmu(const char *name,
195				    const char *parent_name,
196				    u8 cg_sel,
197				    unsigned long rate,
198				    unsigned long flags,
199				    const struct clk_ops *clk_prcmu_ops)
200{
201	struct clk_prcmu *clk;
202	struct clk_init_data clk_prcmu_init;
203	int ret;
204
205	if (!name) {
206		pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
207		return ERR_PTR(-EINVAL);
208	}
209
210	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
211	if (!clk)
212		return ERR_PTR(-ENOMEM);
213
214	clk->cg_sel = cg_sel;
215	clk->opp_requested = 0;
216	/* "rate" can be used for changing the initial frequency */
217	if (rate)
218		prcmu_set_clock_rate(cg_sel, rate);
219
220	clk_prcmu_init.name = name;
221	clk_prcmu_init.ops = clk_prcmu_ops;
222	clk_prcmu_init.flags = flags;
223	clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
224	clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
225	clk->hw.init = &clk_prcmu_init;
226
227	ret = clk_hw_register(NULL, &clk->hw);
228	if (ret)
229		goto free_clk;
230
231	return &clk->hw;
232
233free_clk:
234	kfree(clk);
235	pr_err("clk_prcmu: %s failed to register clk\n", __func__);
236	return ERR_PTR(-ENOMEM);
237}
238
239struct clk_hw *clk_reg_prcmu_scalable(const char *name,
240				      const char *parent_name,
241				      u8 cg_sel,
242				      unsigned long rate,
243				      unsigned long flags)
244{
245	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
246			&clk_prcmu_scalable_ops);
247}
248
249struct clk_hw *clk_reg_prcmu_gate(const char *name,
250				  const char *parent_name,
251				  u8 cg_sel,
252				  unsigned long flags)
253{
254	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
255			&clk_prcmu_gate_ops);
256}
257
258struct clk_hw *clk_reg_prcmu_scalable_rate(const char *name,
259					   const char *parent_name,
260					   u8 cg_sel,
261					   unsigned long rate,
262					   unsigned long flags)
263{
264	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
265			&clk_prcmu_scalable_rate_ops);
266}
267
268struct clk_hw *clk_reg_prcmu_rate(const char *name,
269				  const char *parent_name,
270				  u8 cg_sel,
271				  unsigned long flags)
272{
273	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
274			&clk_prcmu_rate_ops);
275}
276
277struct clk_hw *clk_reg_prcmu_opp_gate(const char *name,
278				      const char *parent_name,
279				      u8 cg_sel,
280				      unsigned long flags)
281{
282	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
283			&clk_prcmu_opp_gate_ops);
284}
285
286struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name,
287					       const char *parent_name,
288					       u8 cg_sel,
289					       unsigned long rate,
290					       unsigned long flags)
291{
292	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
293			&clk_prcmu_opp_volt_scalable_ops);
294}
295
296/* The clkout (external) clock is special and need special ops */
297
298static int clk_prcmu_clkout_prepare(struct clk_hw *hw)
299{
300	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
301
302	return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider);
303}
304
305static void clk_prcmu_clkout_unprepare(struct clk_hw *hw)
306{
307	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
308	int ret;
309
310	/* The clkout clock is disabled by dividing by 0 */
311	ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0);
312	if (ret)
313		pr_err("clk_prcmu: %s failed to disable %s\n", __func__,
314		       clk_hw_get_name(hw));
315}
316
317static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw,
318						  unsigned long parent_rate)
319{
320	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
321
322	return (parent_rate / clk->divider);
323}
324
325static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw)
326{
327	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
328
329	return clk->source;
330}
331
332static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index)
333{
334	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
335
336	clk->source = index;
337	/* Make sure the change reaches the hardware immediately */
338	if (clk_hw_is_prepared(hw))
339		return clk_prcmu_clkout_prepare(hw);
340	return 0;
341}
342
343static const struct clk_ops clk_prcmu_clkout_ops = {
344	.prepare = clk_prcmu_clkout_prepare,
345	.unprepare = clk_prcmu_clkout_unprepare,
346	.recalc_rate = clk_prcmu_clkout_recalc_rate,
347	.determine_rate = clk_hw_determine_rate_no_reparent,
348	.get_parent = clk_prcmu_clkout_get_parent,
349	.set_parent = clk_prcmu_clkout_set_parent,
350};
351
352struct clk_hw *clk_reg_prcmu_clkout(const char *name,
353				    const char * const *parent_names,
354				    int num_parents,
355				    u8 source, u8 divider)
356
357{
358	struct clk_prcmu_clkout *clk;
359	struct clk_init_data clk_prcmu_clkout_init;
360	u8 clkout_id;
361	int ret;
362
363	if (!name) {
364		pr_err("clk_prcmu_clkout: %s invalid arguments passed\n", __func__);
365		return ERR_PTR(-EINVAL);
366	}
367
368	if (!strcmp(name, "clkout1"))
369		clkout_id = 0;
370	else if (!strcmp(name, "clkout2"))
371		clkout_id = 1;
372	else {
373		pr_err("clk_prcmu_clkout: %s bad clock name\n", __func__);
374		return ERR_PTR(-EINVAL);
375	}
376
377	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
378	if (!clk)
379		return ERR_PTR(-ENOMEM);
380
381	clk->clkout_id = clkout_id;
382	clk->source = source;
383	clk->divider = divider;
384
385	clk_prcmu_clkout_init.name = name;
386	clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops;
387	clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE;
388	clk_prcmu_clkout_init.parent_names = parent_names;
389	clk_prcmu_clkout_init.num_parents = num_parents;
390	clk->hw.init = &clk_prcmu_clkout_init;
391
392	ret = clk_hw_register(NULL, &clk->hw);
393	if (ret)
394		goto free_clkout;
395
396	return &clk->hw;
397free_clkout:
398	kfree(clk);
399	pr_err("clk_prcmu_clkout: %s failed to register clk\n", __func__);
400	return ERR_PTR(-ENOMEM);
401}
v6.9.4
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * PRCMU clock implementation for ux500 platform.
  4 *
  5 * Copyright (C) 2012 ST-Ericsson SA
  6 * Author: Ulf Hansson <ulf.hansson@linaro.org>
  7 */
  8
  9#include <linux/clk-provider.h>
 10#include <linux/mfd/dbx500-prcmu.h>
 11#include <linux/slab.h>
 12#include <linux/io.h>
 13#include <linux/err.h>
 14#include "clk.h"
 15
 16#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
 17#define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw)
 18
 19struct clk_prcmu {
 20	struct clk_hw hw;
 21	u8 cg_sel;
 22	int opp_requested;
 23};
 24
 25struct clk_prcmu_clkout {
 26	struct clk_hw hw;
 27	u8 clkout_id;
 28	u8 source;
 29	u8 divider;
 30};
 31
 32/* PRCMU clock operations. */
 33
 34static int clk_prcmu_prepare(struct clk_hw *hw)
 35{
 36	struct clk_prcmu *clk = to_clk_prcmu(hw);
 37
 38	return prcmu_request_clock(clk->cg_sel, true);
 39}
 40
 41static void clk_prcmu_unprepare(struct clk_hw *hw)
 42{
 43	struct clk_prcmu *clk = to_clk_prcmu(hw);
 44	if (prcmu_request_clock(clk->cg_sel, false))
 45		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
 46		       clk_hw_get_name(hw));
 47}
 48
 49static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
 50					   unsigned long parent_rate)
 51{
 52	struct clk_prcmu *clk = to_clk_prcmu(hw);
 53	return prcmu_clock_rate(clk->cg_sel);
 54}
 55
 56static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
 57				 unsigned long *parent_rate)
 58{
 59	struct clk_prcmu *clk = to_clk_prcmu(hw);
 60	return prcmu_round_clock_rate(clk->cg_sel, rate);
 61}
 62
 63static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
 64			      unsigned long parent_rate)
 65{
 66	struct clk_prcmu *clk = to_clk_prcmu(hw);
 67	return prcmu_set_clock_rate(clk->cg_sel, rate);
 68}
 69
 70static int clk_prcmu_opp_prepare(struct clk_hw *hw)
 71{
 72	int err;
 73	struct clk_prcmu *clk = to_clk_prcmu(hw);
 74
 75	if (!clk->opp_requested) {
 76		err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
 77						(char *)clk_hw_get_name(hw),
 78						100);
 79		if (err) {
 80			pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
 81				__func__, clk_hw_get_name(hw));
 82			return err;
 83		}
 84		clk->opp_requested = 1;
 85	}
 86
 87	err = prcmu_request_clock(clk->cg_sel, true);
 88	if (err) {
 89		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
 90					(char *)clk_hw_get_name(hw));
 91		clk->opp_requested = 0;
 92		return err;
 93	}
 94
 95	return 0;
 96}
 97
 98static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
 99{
100	struct clk_prcmu *clk = to_clk_prcmu(hw);
101
102	if (prcmu_request_clock(clk->cg_sel, false)) {
103		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
104			clk_hw_get_name(hw));
105		return;
106	}
107
108	if (clk->opp_requested) {
109		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
110					(char *)clk_hw_get_name(hw));
111		clk->opp_requested = 0;
112	}
113}
114
115static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
116{
117	int err;
118	struct clk_prcmu *clk = to_clk_prcmu(hw);
119
120	if (!clk->opp_requested) {
121		err = prcmu_request_ape_opp_100_voltage(true);
122		if (err) {
123			pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
124				__func__, clk_hw_get_name(hw));
125			return err;
126		}
127		clk->opp_requested = 1;
128	}
129
130	err = prcmu_request_clock(clk->cg_sel, true);
131	if (err) {
132		prcmu_request_ape_opp_100_voltage(false);
133		clk->opp_requested = 0;
134		return err;
135	}
136
137	return 0;
138}
139
140static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
141{
142	struct clk_prcmu *clk = to_clk_prcmu(hw);
143
144	if (prcmu_request_clock(clk->cg_sel, false)) {
145		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
146			clk_hw_get_name(hw));
147		return;
148	}
149
150	if (clk->opp_requested) {
151		prcmu_request_ape_opp_100_voltage(false);
152		clk->opp_requested = 0;
153	}
154}
155
156static const struct clk_ops clk_prcmu_scalable_ops = {
157	.prepare = clk_prcmu_prepare,
158	.unprepare = clk_prcmu_unprepare,
159	.recalc_rate = clk_prcmu_recalc_rate,
160	.round_rate = clk_prcmu_round_rate,
161	.set_rate = clk_prcmu_set_rate,
162};
163
164static const struct clk_ops clk_prcmu_gate_ops = {
165	.prepare = clk_prcmu_prepare,
166	.unprepare = clk_prcmu_unprepare,
167	.recalc_rate = clk_prcmu_recalc_rate,
168};
169
170static const struct clk_ops clk_prcmu_scalable_rate_ops = {
171	.recalc_rate = clk_prcmu_recalc_rate,
172	.round_rate = clk_prcmu_round_rate,
173	.set_rate = clk_prcmu_set_rate,
174};
175
176static const struct clk_ops clk_prcmu_rate_ops = {
177	.recalc_rate = clk_prcmu_recalc_rate,
178};
179
180static const struct clk_ops clk_prcmu_opp_gate_ops = {
181	.prepare = clk_prcmu_opp_prepare,
182	.unprepare = clk_prcmu_opp_unprepare,
183	.recalc_rate = clk_prcmu_recalc_rate,
184};
185
186static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
187	.prepare = clk_prcmu_opp_volt_prepare,
188	.unprepare = clk_prcmu_opp_volt_unprepare,
189	.recalc_rate = clk_prcmu_recalc_rate,
190	.round_rate = clk_prcmu_round_rate,
191	.set_rate = clk_prcmu_set_rate,
192};
193
194static struct clk_hw *clk_reg_prcmu(const char *name,
195				    const char *parent_name,
196				    u8 cg_sel,
197				    unsigned long rate,
198				    unsigned long flags,
199				    const struct clk_ops *clk_prcmu_ops)
200{
201	struct clk_prcmu *clk;
202	struct clk_init_data clk_prcmu_init;
203	int ret;
204
205	if (!name) {
206		pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
207		return ERR_PTR(-EINVAL);
208	}
209
210	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
211	if (!clk)
212		return ERR_PTR(-ENOMEM);
213
214	clk->cg_sel = cg_sel;
215	clk->opp_requested = 0;
216	/* "rate" can be used for changing the initial frequency */
217	if (rate)
218		prcmu_set_clock_rate(cg_sel, rate);
219
220	clk_prcmu_init.name = name;
221	clk_prcmu_init.ops = clk_prcmu_ops;
222	clk_prcmu_init.flags = flags;
223	clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
224	clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
225	clk->hw.init = &clk_prcmu_init;
226
227	ret = clk_hw_register(NULL, &clk->hw);
228	if (ret)
229		goto free_clk;
230
231	return &clk->hw;
232
233free_clk:
234	kfree(clk);
235	pr_err("clk_prcmu: %s failed to register clk\n", __func__);
236	return ERR_PTR(-ENOMEM);
237}
238
239struct clk_hw *clk_reg_prcmu_scalable(const char *name,
240				      const char *parent_name,
241				      u8 cg_sel,
242				      unsigned long rate,
243				      unsigned long flags)
244{
245	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
246			&clk_prcmu_scalable_ops);
247}
248
249struct clk_hw *clk_reg_prcmu_gate(const char *name,
250				  const char *parent_name,
251				  u8 cg_sel,
252				  unsigned long flags)
253{
254	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
255			&clk_prcmu_gate_ops);
256}
257
258struct clk_hw *clk_reg_prcmu_scalable_rate(const char *name,
259					   const char *parent_name,
260					   u8 cg_sel,
261					   unsigned long rate,
262					   unsigned long flags)
263{
264	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
265			&clk_prcmu_scalable_rate_ops);
266}
267
268struct clk_hw *clk_reg_prcmu_rate(const char *name,
269				  const char *parent_name,
270				  u8 cg_sel,
271				  unsigned long flags)
272{
273	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
274			&clk_prcmu_rate_ops);
275}
276
277struct clk_hw *clk_reg_prcmu_opp_gate(const char *name,
278				      const char *parent_name,
279				      u8 cg_sel,
280				      unsigned long flags)
281{
282	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
283			&clk_prcmu_opp_gate_ops);
284}
285
286struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name,
287					       const char *parent_name,
288					       u8 cg_sel,
289					       unsigned long rate,
290					       unsigned long flags)
291{
292	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
293			&clk_prcmu_opp_volt_scalable_ops);
294}
295
296/* The clkout (external) clock is special and need special ops */
297
298static int clk_prcmu_clkout_prepare(struct clk_hw *hw)
299{
300	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
301
302	return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider);
303}
304
305static void clk_prcmu_clkout_unprepare(struct clk_hw *hw)
306{
307	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
308	int ret;
309
310	/* The clkout clock is disabled by dividing by 0 */
311	ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0);
312	if (ret)
313		pr_err("clk_prcmu: %s failed to disable %s\n", __func__,
314		       clk_hw_get_name(hw));
315}
316
317static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw,
318						  unsigned long parent_rate)
319{
320	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
321
322	return (parent_rate / clk->divider);
323}
324
325static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw)
326{
327	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
328
329	return clk->source;
330}
331
332static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index)
333{
334	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
335
336	clk->source = index;
337	/* Make sure the change reaches the hardware immediately */
338	if (clk_hw_is_prepared(hw))
339		return clk_prcmu_clkout_prepare(hw);
340	return 0;
341}
342
343static const struct clk_ops clk_prcmu_clkout_ops = {
344	.prepare = clk_prcmu_clkout_prepare,
345	.unprepare = clk_prcmu_clkout_unprepare,
346	.recalc_rate = clk_prcmu_clkout_recalc_rate,
347	.determine_rate = clk_hw_determine_rate_no_reparent,
348	.get_parent = clk_prcmu_clkout_get_parent,
349	.set_parent = clk_prcmu_clkout_set_parent,
350};
351
352struct clk_hw *clk_reg_prcmu_clkout(const char *name,
353				    const char * const *parent_names,
354				    int num_parents,
355				    u8 source, u8 divider)
356
357{
358	struct clk_prcmu_clkout *clk;
359	struct clk_init_data clk_prcmu_clkout_init;
360	u8 clkout_id;
361	int ret;
362
363	if (!name) {
364		pr_err("clk_prcmu_clkout: %s invalid arguments passed\n", __func__);
365		return ERR_PTR(-EINVAL);
366	}
367
368	if (!strcmp(name, "clkout1"))
369		clkout_id = 0;
370	else if (!strcmp(name, "clkout2"))
371		clkout_id = 1;
372	else {
373		pr_err("clk_prcmu_clkout: %s bad clock name\n", __func__);
374		return ERR_PTR(-EINVAL);
375	}
376
377	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
378	if (!clk)
379		return ERR_PTR(-ENOMEM);
380
381	clk->clkout_id = clkout_id;
382	clk->source = source;
383	clk->divider = divider;
384
385	clk_prcmu_clkout_init.name = name;
386	clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops;
387	clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE;
388	clk_prcmu_clkout_init.parent_names = parent_names;
389	clk_prcmu_clkout_init.num_parents = num_parents;
390	clk->hw.init = &clk_prcmu_clkout_init;
391
392	ret = clk_hw_register(NULL, &clk->hw);
393	if (ret)
394		goto free_clkout;
395
396	return &clk->hw;
397free_clkout:
398	kfree(clk);
399	pr_err("clk_prcmu_clkout: %s failed to register clk\n", __func__);
400	return ERR_PTR(-ENOMEM);
401}