1 /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
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.
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.
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>
21 #include <linux/platform_device.h>
22 #include <linux/regmap.h>
23 #include <linux/slab.h>
24 #include <linux/types.h>
26 #define REG_DIV_CTL1 0x43
27 #define DIV_CTL1_DIV_FACTOR_MASK GENMASK(2, 0)
29 #define REG_EN_CTL 0x46
30 #define REG_EN_MASK BIT(7)
33 struct regmap
*regmap
;
38 unsigned int cxo_period_ns
;
41 static inline struct clkdiv
*to_clkdiv(struct clk_hw
*hw
)
43 return container_of(hw
, struct clkdiv
, hw
);
46 static inline unsigned int div_factor_to_div(unsigned int div_factor
)
51 return 1 << (div_factor
- 1);
54 static inline unsigned int div_to_div_factor(unsigned int div
)
56 return min(ilog2(div
) + 1, 7);
59 static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv
*clkdiv
)
63 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_EN_CTL
, &val
);
65 return val
& REG_EN_MASK
;
69 __spmi_pmic_clkdiv_set_enable_state(struct clkdiv
*clkdiv
, bool enable
,
70 unsigned int div_factor
)
73 unsigned int ns
= clkdiv
->cxo_period_ns
;
74 unsigned int div
= div_factor_to_div(div_factor
);
76 ret
= regmap_update_bits(clkdiv
->regmap
, clkdiv
->base
+ REG_EN_CTL
,
77 REG_EN_MASK
, enable
? REG_EN_MASK
: 0);
82 ndelay((2 + 3 * div
) * ns
);
89 static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv
*clkdiv
, bool enable
)
91 unsigned int div_factor
;
93 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
, &div_factor
);
94 div_factor
&= DIV_CTL1_DIV_FACTOR_MASK
;
96 return __spmi_pmic_clkdiv_set_enable_state(clkdiv
, enable
, div_factor
);
99 static int clk_spmi_pmic_div_enable(struct clk_hw
*hw
)
101 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
105 spin_lock_irqsave(&clkdiv
->lock
, flags
);
106 ret
= spmi_pmic_clkdiv_set_enable_state(clkdiv
, true);
107 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
112 static void clk_spmi_pmic_div_disable(struct clk_hw
*hw
)
114 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
117 spin_lock_irqsave(&clkdiv
->lock
, flags
);
118 spmi_pmic_clkdiv_set_enable_state(clkdiv
, false);
119 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
122 static long clk_spmi_pmic_div_round_rate(struct clk_hw
*hw
, unsigned long rate
,
123 unsigned long *parent_rate
)
125 unsigned int div
, div_factor
;
127 div
= DIV_ROUND_UP(*parent_rate
, rate
);
128 div_factor
= div_to_div_factor(div
);
129 div
= div_factor_to_div(div_factor
);
131 return *parent_rate
/ div
;
135 clk_spmi_pmic_div_recalc_rate(struct clk_hw
*hw
, unsigned long parent_rate
)
137 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
138 unsigned int div_factor
;
140 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
, &div_factor
);
141 div_factor
&= DIV_CTL1_DIV_FACTOR_MASK
;
143 return parent_rate
/ div_factor_to_div(div_factor
);
146 static int clk_spmi_pmic_div_set_rate(struct clk_hw
*hw
, unsigned long rate
,
147 unsigned long parent_rate
)
149 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
150 unsigned int div_factor
= div_to_div_factor(parent_rate
/ rate
);
155 spin_lock_irqsave(&clkdiv
->lock
, flags
);
156 enabled
= is_spmi_pmic_clkdiv_enabled(clkdiv
);
158 ret
= spmi_pmic_clkdiv_set_enable_state(clkdiv
, false);
163 ret
= regmap_update_bits(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
,
164 DIV_CTL1_DIV_FACTOR_MASK
, div_factor
);
169 ret
= __spmi_pmic_clkdiv_set_enable_state(clkdiv
, true,
173 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
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
,
186 struct spmi_pmic_div_clk_cc
{
188 struct clkdiv clks
[];
191 static struct clk_hw
*
192 spmi_pmic_div_clk_hw_get(struct of_phandle_args
*clkspec
, void *data
)
194 struct spmi_pmic_div_clk_cc
*cc
= data
;
195 int idx
= clkspec
->args
[0] - 1; /* Start at 1 instead of 0 */
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
);
203 return &cc
->clks
[idx
].hw
;
206 static int spmi_pmic_clkdiv_probe(struct platform_device
*pdev
)
208 struct spmi_pmic_div_clk_cc
*cc
;
209 struct clk_init_data init
= {};
210 struct clkdiv
*clkdiv
;
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
;
220 ret
= of_property_read_u32(of_node
, "reg", &start
);
222 dev_err(dev
, "reg property reading failed\n");
226 regmap
= dev_get_regmap(dev
->parent
, NULL
);
228 dev_err(dev
, "Couldn't get parent's regmap\n");
232 ret
= of_property_read_u32(of_node
, "qcom,num-clkdivs", &nclks
);
234 dev_err(dev
, "qcom,num-clkdivs property reading failed, ret=%d\n",
242 cc
= devm_kzalloc(dev
, sizeof(*cc
) + sizeof(*cc
->clks
) * nclks
,
248 cxo
= clk_get(dev
, "xo");
251 if (ret
!= -EPROBE_DEFER
)
252 dev_err(dev
, "failed to get xo clock\n");
255 cxo_hz
= clk_get_rate(cxo
);
258 parent_name
= of_clk_get_parent_name(of_node
, 0);
260 dev_err(dev
, "missing parent clock\n");
265 init
.parent_names
= &parent_name
;
266 init
.num_parents
= 1;
267 init
.ops
= &clk_spmi_pmic_div_ops
;
269 for (i
= 0, clkdiv
= cc
->clks
; i
< nclks
; i
++) {
270 snprintf(name
, sizeof(name
), "div_clk%d", i
+ 1);
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
;
278 ret
= devm_clk_hw_register(dev
, &clkdiv
[i
].hw
);
283 return devm_of_clk_add_hw_provider(dev
, spmi_pmic_div_clk_hw_get
, cc
);
286 static const struct of_device_id spmi_pmic_clkdiv_match_table
[] = {
287 { .compatible
= "qcom,spmi-clkdiv" },
290 MODULE_DEVICE_TABLE(of
, spmi_pmic_clkdiv_match_table
);
292 static struct platform_driver spmi_pmic_clkdiv_driver
= {
294 .name
= "qcom,spmi-pmic-clkdiv",
295 .of_match_table
= spmi_pmic_clkdiv_match_table
,
297 .probe
= spmi_pmic_clkdiv_probe
,
299 module_platform_driver(spmi_pmic_clkdiv_driver
);
301 MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver");
302 MODULE_LICENSE("GPL v2");