File Index
Files and directories
Sign in
FILE

clk-cgu.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/x86/clk-cgu.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2020-2022 MaxLinear, Inc.
  4 * Copyright (C) 2020 Intel Corporation.
  5 * Zhu Yixin <yzhu@maxlinear.com>
  6 * Rahul Tanwar <rtanwar@maxlinear.com>
  7 */
  8#include <linux/clk-provider.h>
  9#include <linux/device.h>
 10#include <linux/of.h>
 11
 12#include "clk-cgu.h"
 13
 14#define GATE_HW_REG_STAT(reg)	((reg) + 0x0)
 15#define GATE_HW_REG_EN(reg)	((reg) + 0x4)
 16#define GATE_HW_REG_DIS(reg)	((reg) + 0x8)
 17#define MAX_DDIV_REG	8
 18#define MAX_DIVIDER_VAL 64
 19
 20#define to_lgm_clk_mux(_hw) container_of(_hw, struct lgm_clk_mux, hw)
 21#define to_lgm_clk_divider(_hw) container_of(_hw, struct lgm_clk_divider, hw)
 22#define to_lgm_clk_gate(_hw) container_of(_hw, struct lgm_clk_gate, hw)
 23#define to_lgm_clk_ddiv(_hw) container_of(_hw, struct lgm_clk_ddiv, hw)
 24
 25static struct clk_hw *lgm_clk_register_fixed(struct lgm_clk_provider *ctx,
 26					     const struct lgm_clk_branch *list)
 27{
 28
 29	if (list->div_flags & CLOCK_FLAG_VAL_INIT)
 30		lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift,
 31				list->div_width, list->div_val);
 32
 33	return clk_hw_register_fixed_rate(NULL, list->name,
 34					  list->parent_data[0].name,
 35					  list->flags, list->mux_flags);
 36}
 37
 38static u8 lgm_clk_mux_get_parent(struct clk_hw *hw)
 39{
 40	struct lgm_clk_mux *mux = to_lgm_clk_mux(hw);
 41	u32 val;
 42
 43	if (mux->flags & MUX_CLK_SW)
 44		val = mux->reg;
 45	else
 46		val = lgm_get_clk_val(mux->membase, mux->reg, mux->shift,
 47				      mux->width);
 48	return clk_mux_val_to_index(hw, NULL, mux->flags, val);
 49}
 50
 51static int lgm_clk_mux_set_parent(struct clk_hw *hw, u8 index)
 52{
 53	struct lgm_clk_mux *mux = to_lgm_clk_mux(hw);
 54	u32 val;
 55
 56	val = clk_mux_index_to_val(NULL, mux->flags, index);
 57	if (mux->flags & MUX_CLK_SW)
 58		mux->reg = val;
 59	else
 60		lgm_set_clk_val(mux->membase, mux->reg, mux->shift,
 61				mux->width, val);
 62
 63	return 0;
 64}
 65
 66static int lgm_clk_mux_determine_rate(struct clk_hw *hw,
 67				      struct clk_rate_request *req)
 68{
 69	struct lgm_clk_mux *mux = to_lgm_clk_mux(hw);
 70
 71	return clk_mux_determine_rate_flags(hw, req, mux->flags);
 72}
 73
 74static const struct clk_ops lgm_clk_mux_ops = {
 75	.get_parent = lgm_clk_mux_get_parent,
 76	.set_parent = lgm_clk_mux_set_parent,
 77	.determine_rate = lgm_clk_mux_determine_rate,
 78};
 79
 80static struct clk_hw *
 81lgm_clk_register_mux(struct lgm_clk_provider *ctx,
 82		     const struct lgm_clk_branch *list)
 83{
 84	unsigned long cflags = list->mux_flags;
 85	struct device *dev = ctx->dev;
 86	u8 shift = list->mux_shift;
 87	u8 width = list->mux_width;
 88	struct clk_init_data init = {};
 89	struct lgm_clk_mux *mux;
 90	u32 reg = list->mux_off;
 91	struct clk_hw *hw;
 92	int ret;
 93
 94	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
 95	if (!mux)
 96		return ERR_PTR(-ENOMEM);
 97
 98	init.name = list->name;
 99	init.ops = &lgm_clk_mux_ops;
100	init.flags = list->flags;
101	init.parent_data = list->parent_data;
102	init.num_parents = list->num_parents;
103
104	mux->membase = ctx->membase;
105	mux->reg = reg;
106	mux->shift = shift;
107	mux->width = width;
108	mux->flags = cflags;
109	mux->hw.init = &init;
110
111	hw = &mux->hw;
112	ret = devm_clk_hw_register(dev, hw);
113	if (ret)
114		return ERR_PTR(ret);
115
116	if (cflags & CLOCK_FLAG_VAL_INIT)
117		lgm_set_clk_val(mux->membase, reg, shift, width, list->mux_val);
118
119	return hw;
120}
121
122static unsigned long
123lgm_clk_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
124{
125	struct lgm_clk_divider *divider = to_lgm_clk_divider(hw);
126	unsigned int val;
127
128	val = lgm_get_clk_val(divider->membase, divider->reg,
129			      divider->shift, divider->width);
130
131	return divider_recalc_rate(hw, parent_rate, val, divider->table,
132				   divider->flags, divider->width);
133}
134
135static int lgm_clk_divider_determine_rate(struct clk_hw *hw,
136					  struct clk_rate_request *req)
137{
138	struct lgm_clk_divider *divider = to_lgm_clk_divider(hw);
139
140	return divider_determine_rate(hw, req, divider->table, divider->width,
141				      divider->flags);
142}
143
144static int
145lgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
146			 unsigned long prate)
147{
148	struct lgm_clk_divider *divider = to_lgm_clk_divider(hw);
149	int value;
150
151	value = divider_get_val(rate, prate, divider->table,
152				divider->width, divider->flags);
153	if (value < 0)
154		return value;
155
156	lgm_set_clk_val(divider->membase, divider->reg,
157			divider->shift, divider->width, value);
158
159	return 0;
160}
161
162static int lgm_clk_divider_enable_disable(struct clk_hw *hw, int enable)
163{
164	struct lgm_clk_divider *div = to_lgm_clk_divider(hw);
165
166	if (div->flags != DIV_CLK_NO_MASK)
167		lgm_set_clk_val(div->membase, div->reg, div->shift_gate,
168				div->width_gate, enable);
169	return 0;
170}
171
172static int lgm_clk_divider_enable(struct clk_hw *hw)
173{
174	return lgm_clk_divider_enable_disable(hw, 1);
175}
176
177static void lgm_clk_divider_disable(struct clk_hw *hw)
178{
179	lgm_clk_divider_enable_disable(hw, 0);
180}
181
182static const struct clk_ops lgm_clk_divider_ops = {
183	.recalc_rate = lgm_clk_divider_recalc_rate,
184	.determine_rate = lgm_clk_divider_determine_rate,
185	.set_rate = lgm_clk_divider_set_rate,
186	.enable = lgm_clk_divider_enable,
187	.disable = lgm_clk_divider_disable,
188};
189
190static struct clk_hw *
191lgm_clk_register_divider(struct lgm_clk_provider *ctx,
192			 const struct lgm_clk_branch *list)
193{
194	unsigned long cflags = list->div_flags;
195	struct device *dev = ctx->dev;
196	struct lgm_clk_divider *div;
197	struct clk_init_data init = {};
198	u8 shift = list->div_shift;
199	u8 width = list->div_width;
200	u8 shift_gate = list->div_shift_gate;
201	u8 width_gate = list->div_width_gate;
202	u32 reg = list->div_off;
203	struct clk_hw *hw;
204	int ret;
205
206	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
207	if (!div)
208		return ERR_PTR(-ENOMEM);
209
210	init.name = list->name;
211	init.ops = &lgm_clk_divider_ops;
212	init.flags = list->flags;
213	init.parent_data = list->parent_data;
214	init.num_parents = 1;
215
216	div->membase = ctx->membase;
217	div->reg = reg;
218	div->shift = shift;
219	div->width = width;
220	div->shift_gate	= shift_gate;
221	div->width_gate	= width_gate;
222	div->flags = cflags;
223	div->table = list->div_table;
224	div->hw.init = &init;
225
226	hw = &div->hw;
227	ret = devm_clk_hw_register(dev, hw);
228	if (ret)
229		return ERR_PTR(ret);
230
231	if (cflags & CLOCK_FLAG_VAL_INIT)
232		lgm_set_clk_val(div->membase, reg, shift, width, list->div_val);
233
234	return hw;
235}
236
237static struct clk_hw *
238lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx,
239			      const struct lgm_clk_branch *list)
240{
241	struct clk_hw *hw;
242
243	hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
244					  list->parent_data[0].name, list->flags,
245					  list->mult, list->div);
246	if (IS_ERR(hw))
247		return ERR_CAST(hw);
248
249	if (list->div_flags & CLOCK_FLAG_VAL_INIT)
250		lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift,
251				list->div_width, list->div_val);
252
253	return hw;
254}
255
256static int lgm_clk_gate_enable(struct clk_hw *hw)
257{
258	struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
259	unsigned int reg;
260
261	reg = GATE_HW_REG_EN(gate->reg);
262	lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1);
263
264	return 0;
265}
266
267static void lgm_clk_gate_disable(struct clk_hw *hw)
268{
269	struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
270	unsigned int reg;
271
272	reg = GATE_HW_REG_DIS(gate->reg);
273	lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1);
274}
275
276static int lgm_clk_gate_is_enabled(struct clk_hw *hw)
277{
278	struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
279	unsigned int reg, ret;
280
281	reg = GATE_HW_REG_STAT(gate->reg);
282	ret = lgm_get_clk_val(gate->membase, reg, gate->shift, 1);
283
284	return ret;
285}
286
287static const struct clk_ops lgm_clk_gate_ops = {
288	.enable = lgm_clk_gate_enable,
289	.disable = lgm_clk_gate_disable,
290	.is_enabled = lgm_clk_gate_is_enabled,
291};
292
293static struct clk_hw *
294lgm_clk_register_gate(struct lgm_clk_provider *ctx,
295		      const struct lgm_clk_branch *list)
296{
297	unsigned long cflags = list->gate_flags;
298	const char *pname = list->parent_data[0].name;
299	struct device *dev = ctx->dev;
300	u8 shift = list->gate_shift;
301	struct clk_init_data init = {};
302	struct lgm_clk_gate *gate;
303	u32 reg = list->gate_off;
304	struct clk_hw *hw;
305	int ret;
306
307	gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
308	if (!gate)
309		return ERR_PTR(-ENOMEM);
310
311	init.name = list->name;
312	init.ops = &lgm_clk_gate_ops;
313	init.flags = list->flags;
314	init.parent_names = pname ? &pname : NULL;
315	init.num_parents = pname ? 1 : 0;
316
317	gate->membase = ctx->membase;
318	gate->reg = reg;
319	gate->shift = shift;
320	gate->flags = cflags;
321	gate->hw.init = &init;
322
323	hw = &gate->hw;
324	ret = devm_clk_hw_register(dev, hw);
325	if (ret)
326		return ERR_PTR(ret);
327
328	if (cflags & CLOCK_FLAG_VAL_INIT) {
329		lgm_set_clk_val(gate->membase, reg, shift, 1, list->gate_val);
330	}
331
332	return hw;
333}
334
335int lgm_clk_register_branches(struct lgm_clk_provider *ctx,
336			      const struct lgm_clk_branch *list,
337			      unsigned int nr_clk)
338{
339	struct clk_hw *hw;
340	unsigned int idx;
341
342	for (idx = 0; idx < nr_clk; idx++, list++) {
343		switch (list->type) {
344		case CLK_TYPE_FIXED:
345			hw = lgm_clk_register_fixed(ctx, list);
346			break;
347		case CLK_TYPE_MUX:
348			hw = lgm_clk_register_mux(ctx, list);
349			break;
350		case CLK_TYPE_DIVIDER:
351			hw = lgm_clk_register_divider(ctx, list);
352			break;
353		case CLK_TYPE_FIXED_FACTOR:
354			hw = lgm_clk_register_fixed_factor(ctx, list);
355			break;
356		case CLK_TYPE_GATE:
357			if (list->gate_flags & GATE_CLK_HW) {
358				hw = lgm_clk_register_gate(ctx, list);
359			} else {
360				/*
361				 * GATE_CLKs can be controlled either from
362				 * CGU clk driver i.e. this driver or directly
363				 * from power management driver/daemon. It is
364				 * dependent on the power policy/profile requirements
365				 * of the end product. To override control of gate
366				 * clks from this driver, provide NULL for this index
367				 * of gate clk provider.
368				 */
369				hw = NULL;
370			}
371			break;
372
373		default:
374			dev_err(ctx->dev, "invalid clk type\n");
375			return -EINVAL;
376		}
377
378		if (IS_ERR(hw)) {
379			dev_err(ctx->dev,
380				"register clk: %s, type: %u failed!\n",
381				list->name, list->type);
382			return -EIO;
383		}
384		ctx->clk_data.hws[list->id] = hw;
385	}
386
387	return 0;
388}
389
390static unsigned long
391lgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
392{
393	struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
394	unsigned int div0, div1, exdiv;
395	u64 prate;
396
397	div0 = lgm_get_clk_val(ddiv->membase, ddiv->reg,
398			       ddiv->shift0, ddiv->width0) + 1;
399	div1 = lgm_get_clk_val(ddiv->membase, ddiv->reg,
400			       ddiv->shift1, ddiv->width1) + 1;
401	exdiv = lgm_get_clk_val(ddiv->membase, ddiv->reg,
402				ddiv->shift2, ddiv->width2);
403	prate = (u64)parent_rate;
404	do_div(prate, div0);
405	do_div(prate, div1);
406
407	if (exdiv) {
408		do_div(prate, ddiv->div);
409		prate *= ddiv->mult;
410	}
411
412	return prate;
413}
414
415static int lgm_clk_ddiv_enable(struct clk_hw *hw)
416{
417	struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
418
419	lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate,
420			ddiv->width_gate, 1);
421	return 0;
422}
423
424static void lgm_clk_ddiv_disable(struct clk_hw *hw)
425{
426	struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
427
428	lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate,
429			ddiv->width_gate, 0);
430}
431
432static int
433lgm_clk_get_ddiv_val(u32 div, u32 *ddiv1, u32 *ddiv2)
434{
435	u32 idx, temp;
436
437	*ddiv1 = 1;
438	*ddiv2 = 1;
439
440	if (div > MAX_DIVIDER_VAL)
441		div = MAX_DIVIDER_VAL;
442
443	if (div > 1) {
444		for (idx = 2; idx <= MAX_DDIV_REG; idx++) {
445			temp = DIV_ROUND_UP_ULL((u64)div, idx);
446			if (div % idx == 0 && temp <= MAX_DDIV_REG)
447				break;
448		}
449
450		if (idx > MAX_DDIV_REG)
451			return -EINVAL;
452
453		*ddiv1 = temp;
454		*ddiv2 = idx;
455	}
456
457	return 0;
458}
459
460static int
461lgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate,
462		      unsigned long prate)
463{
464	struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
465	u32 div, ddiv1, ddiv2;
466
467	div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
468
469	if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
470		div = DIV_ROUND_CLOSEST_ULL((u64)div, 5);
471		div = div * 2;
472	}
473
474	if (div <= 0)
475		return -EINVAL;
476
477	if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2))
478		return -EINVAL;
479
480	lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0,
481			ddiv1 - 1);
482
483	lgm_set_clk_val(ddiv->membase, ddiv->reg,  ddiv->shift1, ddiv->width1,
484			ddiv2 - 1);
485
486	return 0;
487}
488
489static int lgm_clk_ddiv_determine_rate(struct clk_hw *hw,
490				       struct clk_rate_request *req)
491{
492	struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
493	u32 div, ddiv1, ddiv2;
494	u64 rate64;
495
496	div = DIV_ROUND_CLOSEST_ULL((u64)req->best_parent_rate, req->rate);
497
498	/* if predivide bit is enabled, modify div by factor of 2.5 */
499	if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
500		div = div * 2;
501		div = DIV_ROUND_CLOSEST_ULL((u64)div, 5);
502	}
503
504	if (div <= 0) {
505		req->rate = req->best_parent_rate;
506
507		return 0;
508	}
509
510	if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2) != 0)
511		if (lgm_clk_get_ddiv_val(div + 1, &ddiv1, &ddiv2) != 0)
512			return -EINVAL;
513
514	rate64 = req->best_parent_rate;
515	do_div(rate64, ddiv1);
516	do_div(rate64, ddiv2);
517
518	/* if predivide bit is enabled, modify rounded rate by factor of 2.5 */
519	if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
520		rate64 = rate64 * 2;
521		rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5);
522	}
523
524	req->rate = rate64;
525
526	return 0;
527}
528
529static const struct clk_ops lgm_clk_ddiv_ops = {
530	.recalc_rate = lgm_clk_ddiv_recalc_rate,
531	.enable	= lgm_clk_ddiv_enable,
532	.disable = lgm_clk_ddiv_disable,
533	.set_rate = lgm_clk_ddiv_set_rate,
534	.determine_rate = lgm_clk_ddiv_determine_rate,
535};
536
537int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx,
538			  const struct lgm_clk_ddiv_data *list,
539			  unsigned int nr_clk)
540{
541	struct device *dev = ctx->dev;
542	struct clk_hw *hw;
543	unsigned int idx;
544	int ret;
545
546	for (idx = 0; idx < nr_clk; idx++, list++) {
547		struct clk_init_data init = {};
548		struct lgm_clk_ddiv *ddiv;
549
550		ddiv = devm_kzalloc(dev, sizeof(*ddiv), GFP_KERNEL);
551		if (!ddiv)
552			return -ENOMEM;
553
554		init.name = list->name;
555		init.ops = &lgm_clk_ddiv_ops;
556		init.flags = list->flags;
557		init.parent_data = list->parent_data;
558		init.num_parents = 1;
559
560		ddiv->membase = ctx->membase;
561		ddiv->reg = list->reg;
562		ddiv->shift0 = list->shift0;
563		ddiv->width0 = list->width0;
564		ddiv->shift1 = list->shift1;
565		ddiv->width1 = list->width1;
566		ddiv->shift_gate = list->shift_gate;
567		ddiv->width_gate = list->width_gate;
568		ddiv->shift2 = list->ex_shift;
569		ddiv->width2 = list->ex_width;
570		ddiv->flags = list->div_flags;
571		ddiv->mult = 2;
572		ddiv->div = 5;
573		ddiv->hw.init = &init;
574
575		hw = &ddiv->hw;
576		ret = devm_clk_hw_register(dev, hw);
577		if (ret) {
578			dev_err(dev, "register clk: %s failed!\n", list->name);
579			return ret;
580		}
581		ctx->clk_data.hws[list->id] = hw;
582	}
583
584	return 0;
585}
~/ Read-only file preview.