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