File Index
Files and directories
Sign in
FILE

clk-super.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/tegra/clk-super.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
  4 */
  5
  6#include <linux/kernel.h>
  7#include <linux/io.h>
  8#include <linux/delay.h>
  9#include <linux/err.h>
 10#include <linux/slab.h>
 11#include <linux/clk-provider.h>
 12
 13#include "clk.h"
 14
 15#define SUPER_STATE_IDLE 0
 16#define SUPER_STATE_RUN 1
 17#define SUPER_STATE_IRQ 2
 18#define SUPER_STATE_FIQ 3
 19
 20#define SUPER_STATE_SHIFT 28
 21#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \
 22			   BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ))	\
 23			  << SUPER_STATE_SHIFT)
 24
 25#define SUPER_LP_DIV2_BYPASS (1 << 16)
 26
 27#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT)
 28#define super_state_to_src_shift(m, s) ((m->width * s))
 29#define super_state_to_src_mask(m) (((1 << m->width) - 1))
 30
 31#define CCLK_SRC_PLLP_OUT0 4
 32#define CCLK_SRC_PLLP_OUT4 5
 33
 34static u8 clk_super_get_parent(struct clk_hw *hw)
 35{
 36	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
 37	u32 val, state;
 38	u8 source, shift;
 39
 40	val = readl_relaxed(mux->reg);
 41
 42	state = val & SUPER_STATE_MASK;
 43
 44	BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
 45	       (state != super_state(SUPER_STATE_IDLE)));
 46	shift = (state == super_state(SUPER_STATE_IDLE)) ?
 47		super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
 48		super_state_to_src_shift(mux, SUPER_STATE_RUN);
 49
 50	source = (val >> shift) & super_state_to_src_mask(mux);
 51
 52	/*
 53	 * If LP_DIV2_BYPASS is not set and PLLX is current parent then
 54	 * PLLX/2 is the input source to CCLKLP.
 55	 */
 56	if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) &&
 57	    (source == mux->pllx_index))
 58		source = mux->div2_index;
 59
 60	return source;
 61}
 62
 63static int clk_super_set_parent(struct clk_hw *hw, u8 index)
 64{
 65	struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
 66	u32 val, state;
 67	int err = 0;
 68	u8 parent_index, shift;
 69	unsigned long flags = 0;
 70
 71	if (mux->lock)
 72		spin_lock_irqsave(mux->lock, flags);
 73
 74	val = readl_relaxed(mux->reg);
 75	state = val & SUPER_STATE_MASK;
 76	BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
 77	       (state != super_state(SUPER_STATE_IDLE)));
 78	shift = (state == super_state(SUPER_STATE_IDLE)) ?
 79		super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
 80		super_state_to_src_shift(mux, SUPER_STATE_RUN);
 81
 82	/*
 83	 * For LP mode super-clock switch between PLLX direct
 84	 * and divided-by-2 outputs is allowed only when other
 85	 * than PLLX clock source is current parent.
 86	 */
 87	if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) ||
 88					       (index == mux->pllx_index))) {
 89		parent_index = clk_super_get_parent(hw);
 90		if ((parent_index == mux->div2_index) ||
 91		    (parent_index == mux->pllx_index)) {
 92			err = -EINVAL;
 93			goto out;
 94		}
 95
 96		val ^= SUPER_LP_DIV2_BYPASS;
 97		writel_relaxed(val, mux->reg);
 98		udelay(2);
 99
100		if (index == mux->div2_index)
101			index = mux->pllx_index;
102	}
103
104	/* enable PLLP branches to CPU before selecting PLLP source */
105	if ((mux->flags & TEGRA210_CPU_CLK) &&
106	    (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
107		tegra_clk_set_pllp_out_cpu(true);
108
109	val &= ~((super_state_to_src_mask(mux)) << shift);
110	val |= (index & (super_state_to_src_mask(mux))) << shift;
111
112	writel_relaxed(val, mux->reg);
113	udelay(2);
114
115	/* disable PLLP branches to CPU if not used */
116	if ((mux->flags & TEGRA210_CPU_CLK) &&
117	    index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
118		tegra_clk_set_pllp_out_cpu(false);
119
120out:
121	if (mux->lock)
122		spin_unlock_irqrestore(mux->lock, flags);
123
124	return err;
125}
126
127static void clk_super_mux_restore_context(struct clk_hw *hw)
128{
129	int parent_id;
130
131	parent_id = clk_hw_get_parent_index(hw);
132	if (WARN_ON(parent_id < 0))
133		return;
134
135	clk_super_set_parent(hw, parent_id);
136}
137
138static const struct clk_ops tegra_clk_super_mux_ops = {
139	.determine_rate = clk_hw_determine_rate_no_reparent,
140	.get_parent = clk_super_get_parent,
141	.set_parent = clk_super_set_parent,
142	.restore_context = clk_super_mux_restore_context,
143};
144
145static int clk_super_determine_rate(struct clk_hw *hw,
146				    struct clk_rate_request *req)
147{
148	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
149	struct clk_hw *div_hw = &super->frac_div.hw;
150
151	__clk_hw_set_clk(div_hw, hw);
152
153	return super->div_ops->determine_rate(div_hw, req);
154}
155
156static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
157					   unsigned long parent_rate)
158{
159	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
160	struct clk_hw *div_hw = &super->frac_div.hw;
161
162	__clk_hw_set_clk(div_hw, hw);
163
164	return super->div_ops->recalc_rate(div_hw, parent_rate);
165}
166
167static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
168			      unsigned long parent_rate)
169{
170	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
171	struct clk_hw *div_hw = &super->frac_div.hw;
172
173	__clk_hw_set_clk(div_hw, hw);
174
175	return super->div_ops->set_rate(div_hw, rate, parent_rate);
176}
177
178static void clk_super_restore_context(struct clk_hw *hw)
179{
180	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
181	struct clk_hw *div_hw = &super->frac_div.hw;
182	int parent_id;
183
184	parent_id = clk_hw_get_parent_index(hw);
185	if (WARN_ON(parent_id < 0))
186		return;
187
188	super->div_ops->restore_context(div_hw);
189	clk_super_set_parent(hw, parent_id);
190}
191
192const struct clk_ops tegra_clk_super_ops = {
193	.get_parent = clk_super_get_parent,
194	.set_parent = clk_super_set_parent,
195	.set_rate = clk_super_set_rate,
196	.determine_rate = clk_super_determine_rate,
197	.recalc_rate = clk_super_recalc_rate,
198	.restore_context = clk_super_restore_context,
199};
200
201struct clk *tegra_clk_register_super_mux(const char *name,
202		const char **parent_names, u8 num_parents,
203		unsigned long flags, void __iomem *reg, u8 clk_super_flags,
204		u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock)
205{
206	struct tegra_clk_super_mux *super;
207	struct clk *clk;
208	struct clk_init_data init;
209
210	super = kzalloc(sizeof(*super), GFP_KERNEL);
211	if (!super)
212		return ERR_PTR(-ENOMEM);
213
214	init.name = name;
215	init.ops = &tegra_clk_super_mux_ops;
216	init.flags = flags;
217	init.parent_names = parent_names;
218	init.num_parents = num_parents;
219
220	super->reg = reg;
221	super->pllx_index = pllx_index;
222	super->div2_index = div2_index;
223	super->lock = lock;
224	super->width = width;
225	super->flags = clk_super_flags;
226
227	/* Data in .init is copied by clk_register(), so stack variable OK */
228	super->hw.init = &init;
229
230	clk = tegra_clk_dev_register(&super->hw);
231	if (IS_ERR(clk))
232		kfree(super);
233
234	return clk;
235}
236
237struct clk *tegra_clk_register_super_clk(const char *name,
238		const char * const *parent_names, u8 num_parents,
239		unsigned long flags, void __iomem *reg, u8 clk_super_flags,
240		spinlock_t *lock)
241{
242	struct tegra_clk_super_mux *super;
243	struct clk *clk;
244	struct clk_init_data init;
245
246	super = kzalloc(sizeof(*super), GFP_KERNEL);
247	if (!super)
248		return ERR_PTR(-ENOMEM);
249
250	init.name = name;
251	init.ops = &tegra_clk_super_ops;
252	init.flags = flags;
253	init.parent_names = parent_names;
254	init.num_parents = num_parents;
255
256	super->reg = reg;
257	super->lock = lock;
258	super->width = 4;
259	super->flags = clk_super_flags;
260	super->frac_div.reg = reg + 4;
261	super->frac_div.shift = 16;
262	super->frac_div.width = 8;
263	super->frac_div.frac_width = 1;
264	super->frac_div.lock = lock;
265	super->div_ops = &tegra_clk_frac_div_ops;
266
267	/* Data in .init is copied by clk_register(), so stack variable OK */
268	super->hw.init = &init;
269
270	clk = clk_register(NULL, &super->hw);
271	if (IS_ERR(clk))
272		kfree(super);
273
274	return clk;
275}
~/ Read-only file preview.