1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
3 * Copyright (c) 2016 AmLogic, Inc.
4 * Author: Michael Turquette <mturquette@baylibre.com>
8 * MultiPhase Locked Loops are outputs from a PLL with additional frequency
9 * scaling capabilities. MPLL rates are calculated as:
11 * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
14 #include <linux/clk-provider.h>
21 static inline struct meson_clk_mpll_data
*
22 meson_clk_mpll_data(struct clk_regmap
*clk
)
24 return (struct meson_clk_mpll_data
*)clk
->data
;
27 static long rate_from_params(unsigned long parent_rate
,
31 unsigned long divisor
= (SDM_DEN
* n2
) + sdm
;
36 return DIV_ROUND_UP_ULL((u64
)parent_rate
* SDM_DEN
, divisor
);
39 static void params_from_rate(unsigned long requested_rate
,
40 unsigned long parent_rate
,
45 uint64_t div
= parent_rate
;
46 uint64_t frac
= do_div(div
, requested_rate
);
50 if (flags
& CLK_MESON_MPLL_ROUND_CLOSEST
)
51 *sdm
= DIV_ROUND_CLOSEST_ULL(frac
, requested_rate
);
53 *sdm
= DIV_ROUND_UP_ULL(frac
, requested_rate
);
55 if (*sdm
== SDM_DEN
) {
63 } else if (div
> N2_MAX
) {
71 static unsigned long mpll_recalc_rate(struct clk_hw
*hw
,
72 unsigned long parent_rate
)
74 struct clk_regmap
*clk
= to_clk_regmap(hw
);
75 struct meson_clk_mpll_data
*mpll
= meson_clk_mpll_data(clk
);
79 sdm
= meson_parm_read(clk
->map
, &mpll
->sdm
);
80 n2
= meson_parm_read(clk
->map
, &mpll
->n2
);
82 rate
= rate_from_params(parent_rate
, sdm
, n2
);
83 return rate
< 0 ? 0 : rate
;
86 static long mpll_round_rate(struct clk_hw
*hw
,
88 unsigned long *parent_rate
)
90 struct clk_regmap
*clk
= to_clk_regmap(hw
);
91 struct meson_clk_mpll_data
*mpll
= meson_clk_mpll_data(clk
);
94 params_from_rate(rate
, *parent_rate
, &sdm
, &n2
, mpll
->flags
);
95 return rate_from_params(*parent_rate
, sdm
, n2
);
98 static int mpll_set_rate(struct clk_hw
*hw
,
100 unsigned long parent_rate
)
102 struct clk_regmap
*clk
= to_clk_regmap(hw
);
103 struct meson_clk_mpll_data
*mpll
= meson_clk_mpll_data(clk
);
104 unsigned int sdm
, n2
;
105 unsigned long flags
= 0;
107 params_from_rate(rate
, parent_rate
, &sdm
, &n2
, mpll
->flags
);
110 spin_lock_irqsave(mpll
->lock
, flags
);
112 __acquire(mpll
->lock
);
114 /* Enable and set the fractional part */
115 meson_parm_write(clk
->map
, &mpll
->sdm
, sdm
);
116 meson_parm_write(clk
->map
, &mpll
->sdm_en
, 1);
118 /* Set additional fractional part enable if required */
119 if (MESON_PARM_APPLICABLE(&mpll
->ssen
))
120 meson_parm_write(clk
->map
, &mpll
->ssen
, 1);
122 /* Set the integer divider part */
123 meson_parm_write(clk
->map
, &mpll
->n2
, n2
);
125 /* Set the magic misc bit if required */
126 if (MESON_PARM_APPLICABLE(&mpll
->misc
))
127 meson_parm_write(clk
->map
, &mpll
->misc
, 1);
130 spin_unlock_irqrestore(mpll
->lock
, flags
);
132 __release(mpll
->lock
);
137 const struct clk_ops meson_clk_mpll_ro_ops
= {
138 .recalc_rate
= mpll_recalc_rate
,
139 .round_rate
= mpll_round_rate
,
142 const struct clk_ops meson_clk_mpll_ops
= {
143 .recalc_rate
= mpll_recalc_rate
,
144 .round_rate
= mpll_round_rate
,
145 .set_rate
= mpll_set_rate
,