1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2018 BayLibre, SAS.
4 * Author: Jerome Brunet <jbrunet@baylibre.com>
5 */
6
7 #include <linux/device.h>
8 #include <linux/module.h>
9 #include <linux/mfd/syscon.h>
10 #include "clk-regmap.h"
11
clk_regmap_init(struct clk_hw * hw)12 int clk_regmap_init(struct clk_hw *hw)
13 {
14 struct clk_regmap *clk = to_clk_regmap(hw);
15 struct device_node *np, *parent_np;
16 struct device *dev;
17
18 /* Allow regmap to be preset as it was historically done */
19 if (clk->map)
20 return 0;
21
22 /*
23 * FIXME: what follows couples the controller implementation
24 * and clk_regmap clock type. This situation is not desirable
25 * but temporary, until the controller is able to register
26 * a hook to initialize a clock type
27 */
28
29 /* Check the usual dev enabled controller with an basic IO regmap */
30 dev = clk_hw_get_dev(hw);
31 if (dev) {
32 clk->map = dev_get_regmap(dev, NULL);
33 if (clk->map)
34 return 0;
35 }
36
37 /* Move on to early and syscon based controllers */
38 np = clk_hw_get_of_node(hw);
39 if (np) {
40 parent_np = of_get_parent(np);
41 clk->map = syscon_node_to_regmap(parent_np);
42 of_node_put(parent_np);
43
44 if (!IS_ERR_OR_NULL(clk->map))
45 return 0;
46 }
47
48 /* Bail out if regmap can't be found */
49 return -EINVAL;
50 }
51 EXPORT_SYMBOL_NS_GPL(clk_regmap_init, "CLK_MESON");
52
clk_regmap_gate_endisable(struct clk_hw * hw,int enable)53 static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
54 {
55 struct clk_regmap *clk = to_clk_regmap(hw);
56 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
57 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
58
59 set ^= enable;
60
61 return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx),
62 set ? BIT(gate->bit_idx) : 0);
63 }
64
clk_regmap_gate_enable(struct clk_hw * hw)65 static int clk_regmap_gate_enable(struct clk_hw *hw)
66 {
67 return clk_regmap_gate_endisable(hw, 1);
68 }
69
clk_regmap_gate_disable(struct clk_hw * hw)70 static void clk_regmap_gate_disable(struct clk_hw *hw)
71 {
72 clk_regmap_gate_endisable(hw, 0);
73 }
74
clk_regmap_gate_is_enabled(struct clk_hw * hw)75 static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
76 {
77 struct clk_regmap *clk = to_clk_regmap(hw);
78 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
79 unsigned int val;
80
81 regmap_read(clk->map, gate->offset, &val);
82 if (gate->flags & CLK_GATE_SET_TO_DISABLE)
83 val ^= BIT(gate->bit_idx);
84
85 val &= BIT(gate->bit_idx);
86
87 return val ? 1 : 0;
88 }
89
90 const struct clk_ops clk_regmap_gate_ops = {
91 .init = clk_regmap_init,
92 .enable = clk_regmap_gate_enable,
93 .disable = clk_regmap_gate_disable,
94 .is_enabled = clk_regmap_gate_is_enabled,
95 };
96 EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, "CLK_MESON");
97
98 const struct clk_ops clk_regmap_gate_ro_ops = {
99 .init = clk_regmap_init,
100 .is_enabled = clk_regmap_gate_is_enabled,
101 };
102 EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, "CLK_MESON");
103
clk_regmap_div_recalc_rate(struct clk_hw * hw,unsigned long prate)104 static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,
105 unsigned long prate)
106 {
107 struct clk_regmap *clk = to_clk_regmap(hw);
108 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
109 unsigned int val;
110 int ret;
111
112 ret = regmap_read(clk->map, div->offset, &val);
113 if (ret)
114 /* Gives a hint that something is wrong */
115 return 0;
116
117 val >>= div->shift;
118 val &= clk_div_mask(div->width);
119 return divider_recalc_rate(hw, prate, val, div->table, div->flags,
120 div->width);
121 }
122
clk_regmap_div_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)123 static int clk_regmap_div_determine_rate(struct clk_hw *hw,
124 struct clk_rate_request *req)
125 {
126 struct clk_regmap *clk = to_clk_regmap(hw);
127 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
128 unsigned int val;
129 int ret;
130
131 /* if read only, just return current value */
132 if (div->flags & CLK_DIVIDER_READ_ONLY) {
133 ret = regmap_read(clk->map, div->offset, &val);
134 if (ret)
135 return ret;
136
137 val >>= div->shift;
138 val &= clk_div_mask(div->width);
139
140 return divider_ro_determine_rate(hw, req, div->table,
141 div->width, div->flags, val);
142 }
143
144 return divider_determine_rate(hw, req, div->table, div->width,
145 div->flags);
146 }
147
clk_regmap_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)148 static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
149 unsigned long parent_rate)
150 {
151 struct clk_regmap *clk = to_clk_regmap(hw);
152 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
153 unsigned int val;
154 int ret;
155
156 ret = divider_get_val(rate, parent_rate, div->table, div->width,
157 div->flags);
158 if (ret < 0)
159 return ret;
160
161 val = (unsigned int)ret << div->shift;
162 return regmap_update_bits(clk->map, div->offset,
163 clk_div_mask(div->width) << div->shift, val);
164 };
165
166 /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
167
168 const struct clk_ops clk_regmap_divider_ops = {
169 .init = clk_regmap_init,
170 .recalc_rate = clk_regmap_div_recalc_rate,
171 .determine_rate = clk_regmap_div_determine_rate,
172 .set_rate = clk_regmap_div_set_rate,
173 };
174 EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, "CLK_MESON");
175
176 const struct clk_ops clk_regmap_divider_ro_ops = {
177 .init = clk_regmap_init,
178 .recalc_rate = clk_regmap_div_recalc_rate,
179 .determine_rate = clk_regmap_div_determine_rate,
180 };
181 EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ro_ops, "CLK_MESON");
182
clk_regmap_mux_get_parent(struct clk_hw * hw)183 static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
184 {
185 struct clk_regmap *clk = to_clk_regmap(hw);
186 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
187 unsigned int val;
188 int ret;
189
190 ret = regmap_read(clk->map, mux->offset, &val);
191 if (ret)
192 return ret;
193
194 val >>= mux->shift;
195 val &= mux->mask;
196 return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
197 }
198
clk_regmap_mux_set_parent(struct clk_hw * hw,u8 index)199 static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
200 {
201 struct clk_regmap *clk = to_clk_regmap(hw);
202 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
203 unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);
204
205 return regmap_update_bits(clk->map, mux->offset,
206 mux->mask << mux->shift,
207 val << mux->shift);
208 }
209
clk_regmap_mux_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)210 static int clk_regmap_mux_determine_rate(struct clk_hw *hw,
211 struct clk_rate_request *req)
212 {
213 struct clk_regmap *clk = to_clk_regmap(hw);
214 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
215
216 return clk_mux_determine_rate_flags(hw, req, mux->flags);
217 }
218
219 const struct clk_ops clk_regmap_mux_ops = {
220 .init = clk_regmap_init,
221 .get_parent = clk_regmap_mux_get_parent,
222 .set_parent = clk_regmap_mux_set_parent,
223 .determine_rate = clk_regmap_mux_determine_rate,
224 };
225 EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, "CLK_MESON");
226
227 const struct clk_ops clk_regmap_mux_ro_ops = {
228 .init = clk_regmap_init,
229 .get_parent = clk_regmap_mux_get_parent,
230 };
231 EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, "CLK_MESON");
232
233 MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
234 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
235 MODULE_LICENSE("GPL");
236 MODULE_IMPORT_NS("CLK_MESON");
237