File Index
Files and directories
Sign in
FILE

ccu_ddn.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/spacemit/ccu_ddn.c C
 1// SPDX-License-Identifier: GPL-2.0-only
 2/*
 3 * Copyright (c) 2024 SpacemiT Technology Co. Ltd
 4 * Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
 5 *
 6 * DDN stands for "Divider Denominator Numerator", it's M/N clock with a
 7 * constant x2 factor. This clock hardware follows the equation below,
 8 *
 9 *	      numerator       Fin
10 *	2 * ------------- = -------
11 *	     denominator      Fout
12 *
13 * Thus, Fout could be calculated with,
14 *
15 *		Fin	denominator
16 *	Fout = ----- * -------------
17 *		 2	 numerator
18 */
19
20#include <linux/clk-provider.h>
21#include <linux/rational.h>
22
23#include "ccu_ddn.h"
24
25static unsigned long ccu_ddn_calc_rate(unsigned long prate, unsigned long num,
26				       unsigned long den, unsigned int pre_div)
27{
28	return prate * den / pre_div / num;
29}
30
31static unsigned long ccu_ddn_calc_best_rate(struct ccu_ddn *ddn,
32					    unsigned long rate, unsigned long prate,
33					    unsigned long *num, unsigned long *den)
34{
35	rational_best_approximation(rate, prate / ddn->pre_div,
36				    ddn->den_mask >> ddn->den_shift,
37				    ddn->num_mask >> ddn->num_shift,
38				    den, num);
39	return ccu_ddn_calc_rate(prate, *num, *den, ddn->pre_div);
40}
41
42static int ccu_ddn_determine_rate(struct clk_hw *hw,
43				  struct clk_rate_request *req)
44{
45	struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
46	unsigned long num, den;
47
48	req->rate = ccu_ddn_calc_best_rate(ddn, req->rate,
49					   req->best_parent_rate, &num, &den);
50
51	return 0;
52}
53
54static unsigned long ccu_ddn_recalc_rate(struct clk_hw *hw, unsigned long prate)
55{
56	struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
57	unsigned int val, num, den;
58
59	val = ccu_read(&ddn->common, ctrl);
60
61	num = (val & ddn->num_mask) >> ddn->num_shift;
62	den = (val & ddn->den_mask) >> ddn->den_shift;
63
64	return ccu_ddn_calc_rate(prate, num, den, ddn->pre_div);
65}
66
67static int ccu_ddn_set_rate(struct clk_hw *hw, unsigned long rate,
68			    unsigned long prate)
69{
70	struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
71	unsigned long num, den;
72
73	ccu_ddn_calc_best_rate(ddn, rate, prate, &num, &den);
74
75	ccu_update(&ddn->common, ctrl,
76		   ddn->num_mask | ddn->den_mask,
77		   (num << ddn->num_shift) | (den << ddn->den_shift));
78
79	return 0;
80}
81
82const struct clk_ops spacemit_ccu_ddn_ops = {
83	.recalc_rate	= ccu_ddn_recalc_rate,
84	.determine_rate = ccu_ddn_determine_rate,
85	.set_rate	= ccu_ddn_set_rate,
86};
87EXPORT_SYMBOL_NS_GPL(spacemit_ccu_ddn_ops, "CLK_SPACEMIT");
~/ Read-only file preview.