File Index
Files and directories
Sign in
FILE

clk-prcmu.c

File preview with syntax highlighting.

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