xref: /linux/drivers/clk/meson/clk-regmap.c (revision b4ada0618eed0fbd1b1630f73deb048c592b06a1)
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 
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 
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 
65 static int clk_regmap_gate_enable(struct clk_hw *hw)
66 {
67 	return clk_regmap_gate_endisable(hw, 1);
68 }
69 
70 static void clk_regmap_gate_disable(struct clk_hw *hw)
71 {
72 	clk_regmap_gate_endisable(hw, 0);
73 }
74 
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 
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 
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 
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 
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 
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 
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