1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2016 Maxime Ripard
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  */
6 #include "ccu.h"
7 #include "ccu_gate.h"
8 #include "ccu_mult.h"
9 
10 struct _ccu_mult
11 {
12     unsigned long   mult, min, max;
13 };
14 
ccu_mult_find_best(unsigned long parent,unsigned long rate,struct _ccu_mult * mult)15 static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
16                                struct _ccu_mult *mult)
17 {
18     int _mult;
19 
20     _mult = rate / parent;
21     if (_mult < mult->min)
22     {
23         _mult = mult->min;
24     }
25 
26     if (_mult > mult->max)
27     {
28         _mult = mult->max;
29     }
30 
31     mult->mult = _mult;
32 }
33 
ccu_mult_round_rate(struct ccu_mux_internal * mux,struct clk_hw * parent,unsigned long * parent_rate,unsigned long rate,void * data)34 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
35         struct clk_hw *parent,
36         unsigned long *parent_rate,
37         unsigned long rate,
38         void *data)
39 {
40     struct ccu_mult *cm = data;
41     struct _ccu_mult _cm;
42 
43     _cm.min = cm->mult.min;
44 
45     if (cm->mult.max)
46     {
47         _cm.max = cm->mult.max;
48     }
49     else
50     {
51         _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
52     }
53 
54     ccu_mult_find_best(*parent_rate, rate, &_cm);
55 
56     return *parent_rate * _cm.mult;
57 }
58 
ccu_mult_disable(struct clk_hw * hw)59 static void ccu_mult_disable(struct clk_hw *hw)
60 {
61     struct ccu_mult *cm = hw_to_ccu_mult(hw);
62 
63     return ccu_gate_helper_disable(&cm->common, cm->enable);
64 }
65 
ccu_mult_enable(struct clk_hw * hw)66 static int ccu_mult_enable(struct clk_hw *hw)
67 {
68     struct ccu_mult *cm = hw_to_ccu_mult(hw);
69 
70     return ccu_gate_helper_enable(&cm->common, cm->enable);
71 }
72 
ccu_mult_is_enabled(struct clk_hw * hw)73 static int ccu_mult_is_enabled(struct clk_hw *hw)
74 {
75     struct ccu_mult *cm = hw_to_ccu_mult(hw);
76 
77     return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
78 }
79 
ccu_mult_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)80 static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
81         unsigned long parent_rate)
82 {
83     struct ccu_mult *cm = hw_to_ccu_mult(hw);
84     unsigned long val;
85     u32 reg;
86 
87     if (ccu_frac_helper_is_enabled(&cm->common, &cm->frac))
88     {
89         return ccu_frac_helper_read_rate(&cm->common, &cm->frac);
90     }
91 
92     reg = readl(cm->common.base + cm->common.reg);
93     val = reg >> cm->mult.shift;
94     val &= (1 << cm->mult.width) - 1;
95 
96     parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
97                   parent_rate);
98 
99     return parent_rate * (val + cm->mult.offset);
100 }
101 
ccu_mult_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)102 static int ccu_mult_determine_rate(struct clk_hw *hw,
103                                    struct clk_rate_request *req)
104 {
105     struct ccu_mult *cm = hw_to_ccu_mult(hw);
106 
107     return ccu_mux_helper_determine_rate(&cm->common, &cm->mux,
108                                          req, ccu_mult_round_rate, cm);
109 }
110 
ccu_mult_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)111 static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
112                              unsigned long parent_rate)
113 {
114     struct ccu_mult *cm = hw_to_ccu_mult(hw);
115     struct _ccu_mult _cm;
116     u32 reg;
117     u32 __cspr;
118 
119     if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate))
120     {
121         ccu_frac_helper_enable(&cm->common, &cm->frac);
122 
123         return ccu_frac_helper_set_rate(&cm->common, &cm->frac,
124                                         rate, cm->lock);
125     }
126     else
127     {
128         ccu_frac_helper_disable(&cm->common, &cm->frac);
129     }
130 
131     parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
132                   parent_rate);
133 
134     _cm.min = cm->mult.min;
135 
136     if (cm->mult.max)
137     {
138         _cm.max = cm->mult.max;
139     }
140     else
141     {
142         _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
143     }
144 
145     ccu_mult_find_best(parent_rate, rate, &_cm);
146 
147     __cspr = hal_spin_lock_irqsave(&cm->common.lock);
148 
149     reg = readl(cm->common.base + cm->common.reg);
150     reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
151     reg |= ((_cm.mult - cm->mult.offset) << cm->mult.shift);
152 
153     writel(reg, cm->common.base + cm->common.reg);
154 
155     hal_spin_unlock_irqrestore(&cm->common.lock, __cspr);
156 
157     ccu_helper_wait_for_lock(&cm->common, cm->lock);
158 
159     return 0;
160 }
161 
ccu_mult_get_parent(struct clk_hw * hw)162 static u8 ccu_mult_get_parent(struct clk_hw *hw)
163 {
164     struct ccu_mult *cm = hw_to_ccu_mult(hw);
165 
166     return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
167 }
168 
ccu_mult_set_parent(struct clk_hw * hw,u8 index)169 static int ccu_mult_set_parent(struct clk_hw *hw, u8 index)
170 {
171     struct ccu_mult *cm = hw_to_ccu_mult(hw);
172 
173     return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
174 }
175 
176 const struct clk_ops ccu_mult_ops =
177 {
178     .disable    = ccu_mult_disable,
179     .enable     = ccu_mult_enable,
180     .is_enabled = ccu_mult_is_enabled,
181 
182     .get_parent = ccu_mult_get_parent,
183     .set_parent = ccu_mult_set_parent,
184 
185     .determine_rate = ccu_mult_determine_rate,
186     .recalc_rate    = ccu_mult_recalc_rate,
187     .set_rate   = ccu_mult_set_rate,
188 };
189