xref: /linux/drivers/clk/qcom/clk-spmi-pmic-div.c (revision bd628c1bed7902ec1f24ba0fe70758949146abbe)
1 /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 and
5  * only version 2 as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12 
13 #include <linux/bitops.h>
14 #include <linux/clk.h>
15 #include <linux/clk-provider.h>
16 #include <linux/delay.h>
17 #include <linux/err.h>
18 #include <linux/log2.h>
19 #include <linux/module.h>
20 #include <linux/of.h>
21 #include <linux/platform_device.h>
22 #include <linux/regmap.h>
23 #include <linux/slab.h>
24 #include <linux/types.h>
25 
26 #define REG_DIV_CTL1			0x43
27 #define DIV_CTL1_DIV_FACTOR_MASK	GENMASK(2, 0)
28 
29 #define REG_EN_CTL			0x46
30 #define REG_EN_MASK			BIT(7)
31 
32 struct clkdiv {
33 	struct regmap		*regmap;
34 	u16			base;
35 	spinlock_t		lock;
36 
37 	struct clk_hw		hw;
38 	unsigned int		cxo_period_ns;
39 };
40 
41 static inline struct clkdiv *to_clkdiv(struct clk_hw *hw)
42 {
43 	return container_of(hw, struct clkdiv, hw);
44 }
45 
46 static inline unsigned int div_factor_to_div(unsigned int div_factor)
47 {
48 	if (!div_factor)
49 		div_factor = 1;
50 
51 	return 1 << (div_factor - 1);
52 }
53 
54 static inline unsigned int div_to_div_factor(unsigned int div)
55 {
56 	return min(ilog2(div) + 1, 7);
57 }
58 
59 static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv *clkdiv)
60 {
61 	unsigned int val = 0;
62 
63 	regmap_read(clkdiv->regmap, clkdiv->base + REG_EN_CTL, &val);
64 
65 	return val & REG_EN_MASK;
66 }
67 
68 static int
69 __spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable,
70 				    unsigned int div_factor)
71 {
72 	int ret;
73 	unsigned int ns = clkdiv->cxo_period_ns;
74 	unsigned int div = div_factor_to_div(div_factor);
75 
76 	ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_EN_CTL,
77 				 REG_EN_MASK, enable ? REG_EN_MASK : 0);
78 	if (ret)
79 		return ret;
80 
81 	if (enable)
82 		ndelay((2 + 3 * div) * ns);
83 	else
84 		ndelay(3 * div * ns);
85 
86 	return 0;
87 }
88 
89 static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable)
90 {
91 	unsigned int div_factor;
92 
93 	regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor);
94 	div_factor &= DIV_CTL1_DIV_FACTOR_MASK;
95 
96 	return __spmi_pmic_clkdiv_set_enable_state(clkdiv, enable, div_factor);
97 }
98 
99 static int clk_spmi_pmic_div_enable(struct clk_hw *hw)
100 {
101 	struct clkdiv *clkdiv = to_clkdiv(hw);
102 	unsigned long flags;
103 	int ret;
104 
105 	spin_lock_irqsave(&clkdiv->lock, flags);
106 	ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, true);
107 	spin_unlock_irqrestore(&clkdiv->lock, flags);
108 
109 	return ret;
110 }
111 
112 static void clk_spmi_pmic_div_disable(struct clk_hw *hw)
113 {
114 	struct clkdiv *clkdiv = to_clkdiv(hw);
115 	unsigned long flags;
116 
117 	spin_lock_irqsave(&clkdiv->lock, flags);
118 	spmi_pmic_clkdiv_set_enable_state(clkdiv, false);
119 	spin_unlock_irqrestore(&clkdiv->lock, flags);
120 }
121 
122 static long clk_spmi_pmic_div_round_rate(struct clk_hw *hw, unsigned long rate,
123 					 unsigned long *parent_rate)
124 {
125 	unsigned int div, div_factor;
126 
127 	div = DIV_ROUND_UP(*parent_rate, rate);
128 	div_factor = div_to_div_factor(div);
129 	div = div_factor_to_div(div_factor);
130 
131 	return *parent_rate / div;
132 }
133 
134 static unsigned long
135 clk_spmi_pmic_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
136 {
137 	struct clkdiv *clkdiv = to_clkdiv(hw);
138 	unsigned int div_factor;
139 
140 	regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor);
141 	div_factor &= DIV_CTL1_DIV_FACTOR_MASK;
142 
143 	return parent_rate / div_factor_to_div(div_factor);
144 }
145 
146 static int clk_spmi_pmic_div_set_rate(struct clk_hw *hw, unsigned long rate,
147 				      unsigned long parent_rate)
148 {
149 	struct clkdiv *clkdiv = to_clkdiv(hw);
150 	unsigned int div_factor = div_to_div_factor(parent_rate / rate);
151 	unsigned long flags;
152 	bool enabled;
153 	int ret;
154 
155 	spin_lock_irqsave(&clkdiv->lock, flags);
156 	enabled = is_spmi_pmic_clkdiv_enabled(clkdiv);
157 	if (enabled) {
158 		ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, false);
159 		if (ret)
160 			goto unlock;
161 	}
162 
163 	ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1,
164 				 DIV_CTL1_DIV_FACTOR_MASK, div_factor);
165 	if (ret)
166 		goto unlock;
167 
168 	if (enabled)
169 		ret = __spmi_pmic_clkdiv_set_enable_state(clkdiv, true,
170 							  div_factor);
171 
172 unlock:
173 	spin_unlock_irqrestore(&clkdiv->lock, flags);
174 
175 	return ret;
176 }
177 
178 static const struct clk_ops clk_spmi_pmic_div_ops = {
179 	.enable = clk_spmi_pmic_div_enable,
180 	.disable = clk_spmi_pmic_div_disable,
181 	.set_rate = clk_spmi_pmic_div_set_rate,
182 	.recalc_rate = clk_spmi_pmic_div_recalc_rate,
183 	.round_rate = clk_spmi_pmic_div_round_rate,
184 };
185 
186 struct spmi_pmic_div_clk_cc {
187 	int		nclks;
188 	struct clkdiv	clks[];
189 };
190 
191 static struct clk_hw *
192 spmi_pmic_div_clk_hw_get(struct of_phandle_args *clkspec, void *data)
193 {
194 	struct spmi_pmic_div_clk_cc *cc = data;
195 	int idx = clkspec->args[0] - 1; /* Start at 1 instead of 0 */
196 
197 	if (idx < 0 || idx >= cc->nclks) {
198 		pr_err("%s: index value %u is invalid; allowed range [1, %d]\n",
199 		       __func__, clkspec->args[0], cc->nclks);
200 		return ERR_PTR(-EINVAL);
201 	}
202 
203 	return &cc->clks[idx].hw;
204 }
205 
206 static int spmi_pmic_clkdiv_probe(struct platform_device *pdev)
207 {
208 	struct spmi_pmic_div_clk_cc *cc;
209 	struct clk_init_data init = {};
210 	struct clkdiv *clkdiv;
211 	struct clk *cxo;
212 	struct regmap *regmap;
213 	struct device *dev = &pdev->dev;
214 	struct device_node *of_node = dev->of_node;
215 	const char *parent_name;
216 	int nclks, i, ret, cxo_hz;
217 	char name[20];
218 	u32 start;
219 
220 	ret = of_property_read_u32(of_node, "reg", &start);
221 	if (ret < 0) {
222 		dev_err(dev, "reg property reading failed\n");
223 		return ret;
224 	}
225 
226 	regmap = dev_get_regmap(dev->parent, NULL);
227 	if (!regmap) {
228 		dev_err(dev, "Couldn't get parent's regmap\n");
229 		return -EINVAL;
230 	}
231 
232 	ret = of_property_read_u32(of_node, "qcom,num-clkdivs", &nclks);
233 	if (ret < 0) {
234 		dev_err(dev, "qcom,num-clkdivs property reading failed, ret=%d\n",
235 			ret);
236 		return ret;
237 	}
238 
239 	if (!nclks)
240 		return -EINVAL;
241 
242 	cc = devm_kzalloc(dev, struct_size(cc, clks, nclks), GFP_KERNEL);
243 	if (!cc)
244 		return -ENOMEM;
245 	cc->nclks = nclks;
246 
247 	cxo = clk_get(dev, "xo");
248 	if (IS_ERR(cxo)) {
249 		ret = PTR_ERR(cxo);
250 		if (ret != -EPROBE_DEFER)
251 			dev_err(dev, "failed to get xo clock\n");
252 		return ret;
253 	}
254 	cxo_hz = clk_get_rate(cxo);
255 	clk_put(cxo);
256 
257 	parent_name = of_clk_get_parent_name(of_node, 0);
258 	if (!parent_name) {
259 		dev_err(dev, "missing parent clock\n");
260 		return -ENODEV;
261 	}
262 
263 	init.name = name;
264 	init.parent_names = &parent_name;
265 	init.num_parents = 1;
266 	init.ops = &clk_spmi_pmic_div_ops;
267 
268 	for (i = 0, clkdiv = cc->clks; i < nclks; i++) {
269 		snprintf(name, sizeof(name), "div_clk%d", i + 1);
270 
271 		spin_lock_init(&clkdiv[i].lock);
272 		clkdiv[i].base = start + i * 0x100;
273 		clkdiv[i].regmap = regmap;
274 		clkdiv[i].cxo_period_ns = NSEC_PER_SEC / cxo_hz;
275 		clkdiv[i].hw.init = &init;
276 
277 		ret = devm_clk_hw_register(dev, &clkdiv[i].hw);
278 		if (ret)
279 			return ret;
280 	}
281 
282 	return devm_of_clk_add_hw_provider(dev, spmi_pmic_div_clk_hw_get, cc);
283 }
284 
285 static const struct of_device_id spmi_pmic_clkdiv_match_table[] = {
286 	{ .compatible = "qcom,spmi-clkdiv" },
287 	{ /* sentinel */ }
288 };
289 MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table);
290 
291 static struct platform_driver spmi_pmic_clkdiv_driver = {
292 	.driver		= {
293 		.name	= "qcom,spmi-pmic-clkdiv",
294 		.of_match_table = spmi_pmic_clkdiv_match_table,
295 	},
296 	.probe		= spmi_pmic_clkdiv_probe,
297 };
298 module_platform_driver(spmi_pmic_clkdiv_driver);
299 
300 MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver");
301 MODULE_LICENSE("GPL v2");
302