1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
5 #include <linux/bitops.h>
7 #include <linux/clk-provider.h>
8 #include <linux/delay.h>
10 #include <linux/log2.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/slab.h>
16 #include <linux/types.h>
18 #define REG_DIV_CTL1 0x43
19 #define DIV_CTL1_DIV_FACTOR_MASK GENMASK(2, 0)
21 #define REG_EN_CTL 0x46
22 #define REG_EN_MASK BIT(7)
25 struct regmap
*regmap
;
30 unsigned int cxo_period_ns
;
33 static inline struct clkdiv
*to_clkdiv(struct clk_hw
*hw
)
35 return container_of(hw
, struct clkdiv
, hw
);
38 static inline unsigned int div_factor_to_div(unsigned int div_factor
)
43 return 1 << (div_factor
- 1);
46 static inline unsigned int div_to_div_factor(unsigned int div
)
48 return min(ilog2(div
) + 1, 7);
51 static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv
*clkdiv
)
55 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_EN_CTL
, &val
);
57 return val
& REG_EN_MASK
;
61 __spmi_pmic_clkdiv_set_enable_state(struct clkdiv
*clkdiv
, bool enable
,
62 unsigned int div_factor
)
65 unsigned int ns
= clkdiv
->cxo_period_ns
;
66 unsigned int div
= div_factor_to_div(div_factor
);
68 ret
= regmap_update_bits(clkdiv
->regmap
, clkdiv
->base
+ REG_EN_CTL
,
69 REG_EN_MASK
, enable
? REG_EN_MASK
: 0);
74 ndelay((2 + 3 * div
) * ns
);
81 static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv
*clkdiv
, bool enable
)
83 unsigned int div_factor
;
85 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
, &div_factor
);
86 div_factor
&= DIV_CTL1_DIV_FACTOR_MASK
;
88 return __spmi_pmic_clkdiv_set_enable_state(clkdiv
, enable
, div_factor
);
91 static int clk_spmi_pmic_div_enable(struct clk_hw
*hw
)
93 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
97 spin_lock_irqsave(&clkdiv
->lock
, flags
);
98 ret
= spmi_pmic_clkdiv_set_enable_state(clkdiv
, true);
99 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
104 static void clk_spmi_pmic_div_disable(struct clk_hw
*hw
)
106 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
109 spin_lock_irqsave(&clkdiv
->lock
, flags
);
110 spmi_pmic_clkdiv_set_enable_state(clkdiv
, false);
111 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
114 static long clk_spmi_pmic_div_round_rate(struct clk_hw
*hw
, unsigned long rate
,
115 unsigned long *parent_rate
)
117 unsigned int div
, div_factor
;
119 div
= DIV_ROUND_UP(*parent_rate
, rate
);
120 div_factor
= div_to_div_factor(div
);
121 div
= div_factor_to_div(div_factor
);
123 return *parent_rate
/ div
;
127 clk_spmi_pmic_div_recalc_rate(struct clk_hw
*hw
, unsigned long parent_rate
)
129 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
130 unsigned int div_factor
;
132 regmap_read(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
, &div_factor
);
133 div_factor
&= DIV_CTL1_DIV_FACTOR_MASK
;
135 return parent_rate
/ div_factor_to_div(div_factor
);
138 static int clk_spmi_pmic_div_set_rate(struct clk_hw
*hw
, unsigned long rate
,
139 unsigned long parent_rate
)
141 struct clkdiv
*clkdiv
= to_clkdiv(hw
);
142 unsigned int div_factor
= div_to_div_factor(parent_rate
/ rate
);
147 spin_lock_irqsave(&clkdiv
->lock
, flags
);
148 enabled
= is_spmi_pmic_clkdiv_enabled(clkdiv
);
150 ret
= spmi_pmic_clkdiv_set_enable_state(clkdiv
, false);
155 ret
= regmap_update_bits(clkdiv
->regmap
, clkdiv
->base
+ REG_DIV_CTL1
,
156 DIV_CTL1_DIV_FACTOR_MASK
, div_factor
);
161 ret
= __spmi_pmic_clkdiv_set_enable_state(clkdiv
, true,
165 spin_unlock_irqrestore(&clkdiv
->lock
, flags
);
170 static const struct clk_ops clk_spmi_pmic_div_ops
= {
171 .enable
= clk_spmi_pmic_div_enable
,
172 .disable
= clk_spmi_pmic_div_disable
,
173 .set_rate
= clk_spmi_pmic_div_set_rate
,
174 .recalc_rate
= clk_spmi_pmic_div_recalc_rate
,
175 .round_rate
= clk_spmi_pmic_div_round_rate
,
178 struct spmi_pmic_div_clk_cc
{
180 struct clkdiv clks
[];
183 static struct clk_hw
*
184 spmi_pmic_div_clk_hw_get(struct of_phandle_args
*clkspec
, void *data
)
186 struct spmi_pmic_div_clk_cc
*cc
= data
;
187 int idx
= clkspec
->args
[0] - 1; /* Start at 1 instead of 0 */
189 if (idx
< 0 || idx
>= cc
->nclks
) {
190 pr_err("%s: index value %u is invalid; allowed range [1, %d]\n",
191 __func__
, clkspec
->args
[0], cc
->nclks
);
192 return ERR_PTR(-EINVAL
);
195 return &cc
->clks
[idx
].hw
;
198 static int spmi_pmic_clkdiv_probe(struct platform_device
*pdev
)
200 struct spmi_pmic_div_clk_cc
*cc
;
201 struct clk_init_data init
= {};
202 struct clkdiv
*clkdiv
;
204 struct regmap
*regmap
;
205 struct device
*dev
= &pdev
->dev
;
206 struct device_node
*of_node
= dev
->of_node
;
207 const char *parent_name
;
208 int nclks
, i
, ret
, cxo_hz
;
212 ret
= of_property_read_u32(of_node
, "reg", &start
);
214 dev_err(dev
, "reg property reading failed\n");
218 regmap
= dev_get_regmap(dev
->parent
, NULL
);
220 dev_err(dev
, "Couldn't get parent's regmap\n");
224 ret
= of_property_read_u32(of_node
, "qcom,num-clkdivs", &nclks
);
226 dev_err(dev
, "qcom,num-clkdivs property reading failed, ret=%d\n",
234 cc
= devm_kzalloc(dev
, struct_size(cc
, clks
, nclks
), GFP_KERNEL
);
239 cxo
= clk_get(dev
, "xo");
242 if (ret
!= -EPROBE_DEFER
)
243 dev_err(dev
, "failed to get xo clock\n");
246 cxo_hz
= clk_get_rate(cxo
);
249 parent_name
= of_clk_get_parent_name(of_node
, 0);
251 dev_err(dev
, "missing parent clock\n");
256 init
.parent_names
= &parent_name
;
257 init
.num_parents
= 1;
258 init
.ops
= &clk_spmi_pmic_div_ops
;
260 for (i
= 0, clkdiv
= cc
->clks
; i
< nclks
; i
++) {
261 snprintf(name
, sizeof(name
), "div_clk%d", i
+ 1);
263 spin_lock_init(&clkdiv
[i
].lock
);
264 clkdiv
[i
].base
= start
+ i
* 0x100;
265 clkdiv
[i
].regmap
= regmap
;
266 clkdiv
[i
].cxo_period_ns
= NSEC_PER_SEC
/ cxo_hz
;
267 clkdiv
[i
].hw
.init
= &init
;
269 ret
= devm_clk_hw_register(dev
, &clkdiv
[i
].hw
);
274 return devm_of_clk_add_hw_provider(dev
, spmi_pmic_div_clk_hw_get
, cc
);
277 static const struct of_device_id spmi_pmic_clkdiv_match_table
[] = {
278 { .compatible
= "qcom,spmi-clkdiv" },
281 MODULE_DEVICE_TABLE(of
, spmi_pmic_clkdiv_match_table
);
283 static struct platform_driver spmi_pmic_clkdiv_driver
= {
285 .name
= "qcom,spmi-pmic-clkdiv",
286 .of_match_table
= spmi_pmic_clkdiv_match_table
,
288 .probe
= spmi_pmic_clkdiv_probe
,
290 module_platform_driver(spmi_pmic_clkdiv_driver
);
292 MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver");
293 MODULE_LICENSE("GPL v2");