Merge branch 'omap-for-v4.16/soc' into omap-for-v4.16/fixes
[sfrench/cifs-2.6.git] / drivers / clk / qcom / clk-spmi-pmic-div.c
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, sizeof(*cc) + sizeof(*cc->clks) * nclks,
243                           GFP_KERNEL);
244         if (!cc)
245                 return -ENOMEM;
246         cc->nclks = nclks;
247
248         cxo = clk_get(dev, "xo");
249         if (IS_ERR(cxo)) {
250                 ret = PTR_ERR(cxo);
251                 if (ret != -EPROBE_DEFER)
252                         dev_err(dev, "failed to get xo clock\n");
253                 return ret;
254         }
255         cxo_hz = clk_get_rate(cxo);
256         clk_put(cxo);
257
258         parent_name = of_clk_get_parent_name(of_node, 0);
259         if (!parent_name) {
260                 dev_err(dev, "missing parent clock\n");
261                 return -ENODEV;
262         }
263
264         init.name = name;
265         init.parent_names = &parent_name;
266         init.num_parents = 1;
267         init.ops = &clk_spmi_pmic_div_ops;
268
269         for (i = 0, clkdiv = cc->clks; i < nclks; i++) {
270                 snprintf(name, sizeof(name), "div_clk%d", i + 1);
271
272                 spin_lock_init(&clkdiv[i].lock);
273                 clkdiv[i].base = start + i * 0x100;
274                 clkdiv[i].regmap = regmap;
275                 clkdiv[i].cxo_period_ns = NSEC_PER_SEC / cxo_hz;
276                 clkdiv[i].hw.init = &init;
277
278                 ret = devm_clk_hw_register(dev, &clkdiv[i].hw);
279                 if (ret)
280                         return ret;
281         }
282
283         return devm_of_clk_add_hw_provider(dev, spmi_pmic_div_clk_hw_get, cc);
284 }
285
286 static const struct of_device_id spmi_pmic_clkdiv_match_table[] = {
287         { .compatible = "qcom,spmi-clkdiv" },
288         { /* sentinel */ }
289 };
290 MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table);
291
292 static struct platform_driver spmi_pmic_clkdiv_driver = {
293         .driver         = {
294                 .name   = "qcom,spmi-pmic-clkdiv",
295                 .of_match_table = spmi_pmic_clkdiv_match_table,
296         },
297         .probe          = spmi_pmic_clkdiv_probe,
298 };
299 module_platform_driver(spmi_pmic_clkdiv_driver);
300
301 MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver");
302 MODULE_LICENSE("GPL v2");