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