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