1 // SPDX-License-Identifier: GPL-2.0
5 * Clock driver for LS1028A Display output interfaces(LCD, DPHY).
8 #include <linux/clk-provider.h>
9 #include <linux/device.h>
10 #include <linux/module.h>
11 #include <linux/err.h>
13 #include <linux/iopoll.h>
15 #include <linux/of_address.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 #include <linux/bitfield.h>
21 /* PLLDIG register offsets and bit masks */
22 #define PLLDIG_REG_PLLSR 0x24
23 #define PLLDIG_LOCK_MASK BIT(2)
24 #define PLLDIG_REG_PLLDV 0x28
25 #define PLLDIG_MFD_MASK GENMASK(7, 0)
26 #define PLLDIG_RFDPHI1_MASK GENMASK(30, 25)
27 #define PLLDIG_REG_PLLFM 0x2c
28 #define PLLDIG_SSCGBYP_ENABLE BIT(30)
29 #define PLLDIG_REG_PLLFD 0x30
30 #define PLLDIG_FDEN BIT(30)
31 #define PLLDIG_FRAC_MASK GENMASK(15, 0)
32 #define PLLDIG_REG_PLLCAL1 0x38
33 #define PLLDIG_REG_PLLCAL2 0x3c
35 /* Range of the VCO frequencies, in Hz */
36 #define PLLDIG_MIN_VCO_FREQ 650000000
37 #define PLLDIG_MAX_VCO_FREQ 1300000000
39 /* Range of the output frequencies, in Hz */
40 #define PHI1_MIN_FREQ 27000000UL
41 #define PHI1_MAX_FREQ 600000000UL
43 /* Maximum value of the reduced frequency divider */
44 #define MAX_RFDPHI1 63UL
46 /* Best value of multiplication factor divider */
47 #define PLLDIG_DEFAULT_MFD 44
50 * Denominator part of the fractional part of the
51 * loop multiplication factor.
55 static const struct clk_parent_data parent_data
[] = {
62 unsigned int vco_freq
;
65 #define to_clk_plldig(_hw) container_of(_hw, struct clk_plldig, hw)
67 static int plldig_enable(struct clk_hw
*hw
)
69 struct clk_plldig
*data
= to_clk_plldig(hw
);
72 val
= readl(data
->regs
+ PLLDIG_REG_PLLFM
);
74 * Use Bypass mode with PLL off by default, the frequency overshoot
75 * detector output was disable. SSCG Bypass mode should be enable.
77 val
|= PLLDIG_SSCGBYP_ENABLE
;
78 writel(val
, data
->regs
+ PLLDIG_REG_PLLFM
);
83 static void plldig_disable(struct clk_hw
*hw
)
85 struct clk_plldig
*data
= to_clk_plldig(hw
);
88 val
= readl(data
->regs
+ PLLDIG_REG_PLLFM
);
90 val
&= ~PLLDIG_SSCGBYP_ENABLE
;
91 val
|= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE
, 0x0);
93 writel(val
, data
->regs
+ PLLDIG_REG_PLLFM
);
96 static int plldig_is_enabled(struct clk_hw
*hw
)
98 struct clk_plldig
*data
= to_clk_plldig(hw
);
100 return readl(data
->regs
+ PLLDIG_REG_PLLFM
) &
101 PLLDIG_SSCGBYP_ENABLE
;
104 static unsigned long plldig_recalc_rate(struct clk_hw
*hw
,
105 unsigned long parent_rate
)
107 struct clk_plldig
*data
= to_clk_plldig(hw
);
110 val
= readl(data
->regs
+ PLLDIG_REG_PLLDV
);
112 /* Check if PLL is bypassed */
113 if (val
& PLLDIG_SSCGBYP_ENABLE
)
116 rfdphi1
= FIELD_GET(PLLDIG_RFDPHI1_MASK
, val
);
119 * If RFDPHI1 has a value of 1 the VCO frequency is also divided by
125 return DIV_ROUND_UP(data
->vco_freq
, rfdphi1
);
128 static unsigned long plldig_calc_target_div(unsigned long vco_freq
,
129 unsigned long target_rate
)
133 div
= DIV_ROUND_CLOSEST(vco_freq
, target_rate
);
134 div
= clamp(div
, 1UL, MAX_RFDPHI1
);
139 static int plldig_determine_rate(struct clk_hw
*hw
,
140 struct clk_rate_request
*req
)
142 struct clk_plldig
*data
= to_clk_plldig(hw
);
145 req
->rate
= clamp(req
->rate
, PHI1_MIN_FREQ
, PHI1_MAX_FREQ
);
146 div
= plldig_calc_target_div(data
->vco_freq
, req
->rate
);
147 req
->rate
= DIV_ROUND_UP(data
->vco_freq
, div
);
152 static int plldig_set_rate(struct clk_hw
*hw
, unsigned long rate
,
153 unsigned long parent_rate
)
155 struct clk_plldig
*data
= to_clk_plldig(hw
);
156 unsigned int val
, cond
;
157 unsigned int rfdphi1
;
159 rate
= clamp(rate
, PHI1_MIN_FREQ
, PHI1_MAX_FREQ
);
160 rfdphi1
= plldig_calc_target_div(data
->vco_freq
, rate
);
162 /* update the divider value */
163 val
= readl(data
->regs
+ PLLDIG_REG_PLLDV
);
164 val
&= ~PLLDIG_RFDPHI1_MASK
;
165 val
|= FIELD_PREP(PLLDIG_RFDPHI1_MASK
, rfdphi1
);
166 writel(val
, data
->regs
+ PLLDIG_REG_PLLDV
);
168 /* waiting for old lock state to clear */
171 /* Wait until PLL is locked or timeout */
172 return readl_poll_timeout_atomic(data
->regs
+ PLLDIG_REG_PLLSR
, cond
,
173 cond
& PLLDIG_LOCK_MASK
, 0,
177 static const struct clk_ops plldig_clk_ops
= {
178 .enable
= plldig_enable
,
179 .disable
= plldig_disable
,
180 .is_enabled
= plldig_is_enabled
,
181 .recalc_rate
= plldig_recalc_rate
,
182 .determine_rate
= plldig_determine_rate
,
183 .set_rate
= plldig_set_rate
,
186 static int plldig_init(struct clk_hw
*hw
)
188 struct clk_plldig
*data
= to_clk_plldig(hw
);
189 struct clk_hw
*parent
= clk_hw_get_parent(hw
);
190 unsigned long parent_rate
;
192 unsigned long long lltmp
;
193 unsigned int mfd
, fracdiv
= 0;
198 parent_rate
= clk_hw_get_rate(parent
);
200 if (data
->vco_freq
) {
201 mfd
= data
->vco_freq
/ parent_rate
;
202 lltmp
= data
->vco_freq
% parent_rate
;
204 do_div(lltmp
, parent_rate
);
207 mfd
= PLLDIG_DEFAULT_MFD
;
208 data
->vco_freq
= parent_rate
* mfd
;
211 val
= FIELD_PREP(PLLDIG_MFD_MASK
, mfd
);
212 writel(val
, data
->regs
+ PLLDIG_REG_PLLDV
);
214 /* Enable fractional divider */
216 val
= FIELD_PREP(PLLDIG_FRAC_MASK
, fracdiv
);
218 writel(val
, data
->regs
+ PLLDIG_REG_PLLFD
);
224 static int plldig_clk_probe(struct platform_device
*pdev
)
226 struct clk_plldig
*data
;
227 struct device
*dev
= &pdev
->dev
;
230 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
234 data
->regs
= devm_platform_ioremap_resource(pdev
, 0);
235 if (IS_ERR(data
->regs
))
236 return PTR_ERR(data
->regs
);
238 data
->hw
.init
= CLK_HW_INIT_PARENTS_DATA("dpclk",
243 ret
= devm_clk_hw_register(dev
, &data
->hw
);
245 dev_err(dev
, "failed to register %s clock\n",
250 ret
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_simple_get
,
253 dev_err(dev
, "unable to add clk provider\n");
258 * The frequency of the VCO cannot be changed during runtime.
259 * Therefore, let the user specify a desired frequency.
261 if (!of_property_read_u32(dev
->of_node
, "fsl,vco-hz",
263 if (data
->vco_freq
< PLLDIG_MIN_VCO_FREQ
||
264 data
->vco_freq
> PLLDIG_MAX_VCO_FREQ
)
268 return plldig_init(&data
->hw
);
271 static const struct of_device_id plldig_clk_id
[] = {
272 { .compatible
= "fsl,ls1028a-plldig" },
275 MODULE_DEVICE_TABLE(of
, plldig_clk_id
);
277 static struct platform_driver plldig_clk_driver
= {
279 .name
= "plldig-clock",
280 .of_match_table
= plldig_clk_id
,
282 .probe
= plldig_clk_probe
,
284 module_platform_driver(plldig_clk_driver
);
286 MODULE_LICENSE("GPL v2");
287 MODULE_AUTHOR("Wen He <wen.he_1@nxp.com>");
288 MODULE_DESCRIPTION("LS1028A Display output interface pixel clock driver");