File Index
Files and directories
Sign in
FILE

clk-aux-synth.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/spear/clk-aux-synth.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2012 ST Microelectronics
  4 * Viresh Kumar <vireshk@kernel.org>
  5 *
  6 * Auxiliary Synthesizer clock implementation
  7 */
  8
  9#define pr_fmt(fmt) "clk-aux-synth: " fmt
 10
 11#include <linux/clk-provider.h>
 12#include <linux/slab.h>
 13#include <linux/io.h>
 14#include <linux/err.h>
 15#include "clk.h"
 16
 17/*
 18 * DOC: Auxiliary Synthesizer clock
 19 *
 20 * Aux synth gives rate for different values of eq, x and y
 21 *
 22 * Fout from synthesizer can be given from two equations:
 23 * Fout1 = (Fin * X/Y)/2		EQ1
 24 * Fout2 = Fin * X/Y			EQ2
 25 */
 26
 27#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw)
 28
 29static const  struct aux_clk_masks default_aux_masks = {
 30	.eq_sel_mask = AUX_EQ_SEL_MASK,
 31	.eq_sel_shift = AUX_EQ_SEL_SHIFT,
 32	.eq1_mask = AUX_EQ1_SEL,
 33	.eq2_mask = AUX_EQ2_SEL,
 34	.xscale_sel_mask = AUX_XSCALE_MASK,
 35	.xscale_sel_shift = AUX_XSCALE_SHIFT,
 36	.yscale_sel_mask = AUX_YSCALE_MASK,
 37	.yscale_sel_shift = AUX_YSCALE_SHIFT,
 38	.enable_bit = AUX_SYNT_ENB,
 39};
 40
 41static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate,
 42		int index)
 43{
 44	struct clk_aux *aux = to_clk_aux(hw);
 45	struct aux_rate_tbl *rtbl = aux->rtbl;
 46	u8 eq = rtbl[index].eq ? 1 : 2;
 47
 48	return (((prate / 10000) * rtbl[index].xscale) /
 49			(rtbl[index].yscale * eq)) * 10000;
 50}
 51
 52static int clk_aux_determine_rate(struct clk_hw *hw,
 53				  struct clk_rate_request *req)
 54{
 55	struct clk_aux *aux = to_clk_aux(hw);
 56	int unused;
 57
 58	req->rate = clk_round_rate_index(hw, req->rate, req->best_parent_rate,
 59					 aux_calc_rate, aux->rtbl_cnt, &unused);
 60
 61	return 0;
 62}
 63
 64static unsigned long clk_aux_recalc_rate(struct clk_hw *hw,
 65		unsigned long parent_rate)
 66{
 67	struct clk_aux *aux = to_clk_aux(hw);
 68	unsigned int num = 1, den = 1, val, eqn;
 69	unsigned long flags = 0;
 70
 71	if (aux->lock)
 72		spin_lock_irqsave(aux->lock, flags);
 73
 74	val = readl_relaxed(aux->reg);
 75
 76	if (aux->lock)
 77		spin_unlock_irqrestore(aux->lock, flags);
 78
 79	eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask;
 80	if (eqn == aux->masks->eq1_mask)
 81		den = 2;
 82
 83	/* calculate numerator */
 84	num = (val >> aux->masks->xscale_sel_shift) &
 85		aux->masks->xscale_sel_mask;
 86
 87	/* calculate denominator */
 88	den *= (val >> aux->masks->yscale_sel_shift) &
 89		aux->masks->yscale_sel_mask;
 90
 91	if (!den)
 92		return 0;
 93
 94	return (((parent_rate / 10000) * num) / den) * 10000;
 95}
 96
 97/* Configures new clock rate of aux */
 98static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate,
 99				unsigned long prate)
100{
101	struct clk_aux *aux = to_clk_aux(hw);
102	struct aux_rate_tbl *rtbl = aux->rtbl;
103	unsigned long val, flags = 0;
104	int i;
105
106	clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt,
107			&i);
108
109	if (aux->lock)
110		spin_lock_irqsave(aux->lock, flags);
111
112	val = readl_relaxed(aux->reg) &
113		~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift);
114	val |= (rtbl[i].eq & aux->masks->eq_sel_mask) <<
115		aux->masks->eq_sel_shift;
116	val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift);
117	val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) <<
118		aux->masks->xscale_sel_shift;
119	val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift);
120	val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) <<
121		aux->masks->yscale_sel_shift;
122	writel_relaxed(val, aux->reg);
123
124	if (aux->lock)
125		spin_unlock_irqrestore(aux->lock, flags);
126
127	return 0;
128}
129
130static const struct clk_ops clk_aux_ops = {
131	.recalc_rate = clk_aux_recalc_rate,
132	.determine_rate = clk_aux_determine_rate,
133	.set_rate = clk_aux_set_rate,
134};
135
136struct clk *clk_register_aux(const char *aux_name, const char *gate_name,
137		const char *parent_name, unsigned long flags, void __iomem *reg,
138	        const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl,
139		u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk)
140{
141	struct clk_aux *aux;
142	struct clk_init_data init;
143	struct clk *clk;
144
145	if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
146		pr_err("Invalid arguments passed");
147		return ERR_PTR(-EINVAL);
148	}
149
150	aux = kzalloc(sizeof(*aux), GFP_KERNEL);
151	if (!aux)
152		return ERR_PTR(-ENOMEM);
153
154	/* struct clk_aux assignments */
155	if (!masks)
156		aux->masks = &default_aux_masks;
157	else
158		aux->masks = masks;
159
160	aux->reg = reg;
161	aux->rtbl = rtbl;
162	aux->rtbl_cnt = rtbl_cnt;
163	aux->lock = lock;
164	aux->hw.init = &init;
165
166	init.name = aux_name;
167	init.ops = &clk_aux_ops;
168	init.flags = flags;
169	init.parent_names = &parent_name;
170	init.num_parents = 1;
171
172	clk = clk_register(NULL, &aux->hw);
173	if (IS_ERR_OR_NULL(clk))
174		goto free_aux;
175
176	if (gate_name) {
177		struct clk *tgate_clk;
178
179		tgate_clk = clk_register_gate(NULL, gate_name, aux_name,
180				CLK_SET_RATE_PARENT, reg,
181				aux->masks->enable_bit, 0, lock);
182		if (IS_ERR_OR_NULL(tgate_clk))
183			goto free_aux;
184
185		if (gate_clk)
186			*gate_clk = tgate_clk;
187	}
188
189	return clk;
190
191free_aux:
192	kfree(aux);
193	pr_err("clk register failed\n");
194
195	return NULL;
196}
~/ Read-only file preview.