1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2016 Maxime Ripard
4*4882a593Smuzhiyun * Maxime Ripard <maxime.ripard@free-electrons.com>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/clk-provider.h>
8*4882a593Smuzhiyun #include <linux/io.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include "ccu_gate.h"
11*4882a593Smuzhiyun #include "ccu_div.h"
12*4882a593Smuzhiyun
ccu_div_round_rate(struct ccu_mux_internal * mux,struct clk_hw * parent,unsigned long * parent_rate,unsigned long rate,void * data)13*4882a593Smuzhiyun static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
14*4882a593Smuzhiyun struct clk_hw *parent,
15*4882a593Smuzhiyun unsigned long *parent_rate,
16*4882a593Smuzhiyun unsigned long rate,
17*4882a593Smuzhiyun void *data)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun struct ccu_div *cd = data;
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
22*4882a593Smuzhiyun rate *= cd->fixed_post_div;
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun rate = divider_round_rate_parent(&cd->common.hw, parent,
25*4882a593Smuzhiyun rate, parent_rate,
26*4882a593Smuzhiyun cd->div.table, cd->div.width,
27*4882a593Smuzhiyun cd->div.flags);
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
30*4882a593Smuzhiyun rate /= cd->fixed_post_div;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun return rate;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
ccu_div_disable(struct clk_hw * hw)35*4882a593Smuzhiyun static void ccu_div_disable(struct clk_hw *hw)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun return ccu_gate_helper_disable(&cd->common, cd->enable);
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
ccu_div_enable(struct clk_hw * hw)42*4882a593Smuzhiyun static int ccu_div_enable(struct clk_hw *hw)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun return ccu_gate_helper_enable(&cd->common, cd->enable);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
ccu_div_is_enabled(struct clk_hw * hw)49*4882a593Smuzhiyun static int ccu_div_is_enabled(struct clk_hw *hw)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun return ccu_gate_helper_is_enabled(&cd->common, cd->enable);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
ccu_div_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)56*4882a593Smuzhiyun static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
57*4882a593Smuzhiyun unsigned long parent_rate)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
60*4882a593Smuzhiyun unsigned long val;
61*4882a593Smuzhiyun u32 reg;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun reg = readl(cd->common.base + cd->common.reg);
64*4882a593Smuzhiyun val = reg >> cd->div.shift;
65*4882a593Smuzhiyun val &= (1 << cd->div.width) - 1;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
68*4882a593Smuzhiyun parent_rate);
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun val = divider_recalc_rate(hw, parent_rate, val, cd->div.table,
71*4882a593Smuzhiyun cd->div.flags, cd->div.width);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
74*4882a593Smuzhiyun val /= cd->fixed_post_div;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun return val;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
ccu_div_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)79*4882a593Smuzhiyun static int ccu_div_determine_rate(struct clk_hw *hw,
80*4882a593Smuzhiyun struct clk_rate_request *req)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
85*4882a593Smuzhiyun req, ccu_div_round_rate, cd);
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
ccu_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)88*4882a593Smuzhiyun static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
89*4882a593Smuzhiyun unsigned long parent_rate)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
92*4882a593Smuzhiyun unsigned long flags;
93*4882a593Smuzhiyun unsigned long val;
94*4882a593Smuzhiyun u32 reg;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
97*4882a593Smuzhiyun parent_rate);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
100*4882a593Smuzhiyun rate *= cd->fixed_post_div;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
103*4882a593Smuzhiyun cd->div.flags);
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun spin_lock_irqsave(cd->common.lock, flags);
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun reg = readl(cd->common.base + cd->common.reg);
108*4882a593Smuzhiyun reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun writel(reg | (val << cd->div.shift),
111*4882a593Smuzhiyun cd->common.base + cd->common.reg);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun spin_unlock_irqrestore(cd->common.lock, flags);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
ccu_div_get_parent(struct clk_hw * hw)118*4882a593Smuzhiyun static u8 ccu_div_get_parent(struct clk_hw *hw)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun return ccu_mux_helper_get_parent(&cd->common, &cd->mux);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
ccu_div_set_parent(struct clk_hw * hw,u8 index)125*4882a593Smuzhiyun static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct ccu_div *cd = hw_to_ccu_div(hw);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun const struct clk_ops ccu_div_ops = {
133*4882a593Smuzhiyun .disable = ccu_div_disable,
134*4882a593Smuzhiyun .enable = ccu_div_enable,
135*4882a593Smuzhiyun .is_enabled = ccu_div_is_enabled,
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun .get_parent = ccu_div_get_parent,
138*4882a593Smuzhiyun .set_parent = ccu_div_set_parent,
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun .determine_rate = ccu_div_determine_rate,
141*4882a593Smuzhiyun .recalc_rate = ccu_div_recalc_rate,
142*4882a593Smuzhiyun .set_rate = ccu_div_set_rate,
143*4882a593Smuzhiyun };
144