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