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/platform_device.h>
16 #include <linux/slab.h>
17 #include <linux/bitfield.h>
19 /* PLLDIG register offsets and bit masks */
20 #define PLLDIG_REG_PLLSR 0x24
21 #define PLLDIG_LOCK_MASK BIT(2)
22 #define PLLDIG_REG_PLLDV 0x28
23 #define PLLDIG_MFD_MASK GENMASK(7, 0)
24 #define PLLDIG_RFDPHI1_MASK GENMASK(30, 25)
25 #define PLLDIG_REG_PLLFM 0x2c
26 #define PLLDIG_SSCGBYP_ENABLE BIT(30)
27 #define PLLDIG_REG_PLLFD 0x30
28 #define PLLDIG_FDEN BIT(30)
29 #define PLLDIG_FRAC_MASK GENMASK(15, 0)
30 #define PLLDIG_REG_PLLCAL1 0x38
31 #define PLLDIG_REG_PLLCAL2 0x3c
33 /* Range of the VCO frequencies, in Hz */
34 #define PLLDIG_MIN_VCO_FREQ 650000000
35 #define PLLDIG_MAX_VCO_FREQ 1300000000
37 /* Range of the output frequencies, in Hz */
38 #define PHI1_MIN_FREQ 27000000UL
39 #define PHI1_MAX_FREQ 600000000UL
41 /* Maximum value of the reduced frequency divider */
42 #define MAX_RFDPHI1 63UL
44 /* Best value of multiplication factor divider */
45 #define PLLDIG_DEFAULT_MFD 44
48 * Denominator part of the fractional part of the
49 * loop multiplication factor.
53 static const struct clk_parent_data parent_data
[] = {
60 unsigned int vco_freq
;
63 #define to_clk_plldig(_hw) container_of(_hw, struct clk_plldig, hw)
65 static int plldig_enable(struct clk_hw
*hw
)
67 struct clk_plldig
*data
= to_clk_plldig(hw
);
70 val
= readl(data
->regs
+ PLLDIG_REG_PLLFM
);
72 * Use Bypass mode with PLL off by default, the frequency overshoot
73 * detector output was disable. SSCG Bypass mode should be enable.
75 val
|= PLLDIG_SSCGBYP_ENABLE
;
76 writel(val
, data
->regs
+ PLLDIG_REG_PLLFM
);
81 static void plldig_disable(struct clk_hw
*hw
)
83 struct clk_plldig
*data
= to_clk_plldig(hw
);
86 val
= readl(data
->regs
+ PLLDIG_REG_PLLFM
);
88 val
&= ~PLLDIG_SSCGBYP_ENABLE
;
89 val
|= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE
, 0x0);
91 writel(val
, data
->regs
+ PLLDIG_REG_PLLFM
);
94 static int plldig_is_enabled(struct clk_hw
*hw
)
96 struct clk_plldig
*data
= to_clk_plldig(hw
);
98 return readl(data
->regs
+ PLLDIG_REG_PLLFM
) &
99 PLLDIG_SSCGBYP_ENABLE
;
102 static unsigned long plldig_recalc_rate(struct clk_hw
*hw
,
103 unsigned long parent_rate
)
105 struct clk_plldig
*data
= to_clk_plldig(hw
);
108 val
= readl(data
->regs
+ PLLDIG_REG_PLLDV
);
110 /* Check if PLL is bypassed */
111 if (val
& PLLDIG_SSCGBYP_ENABLE
)
114 rfdphi1
= FIELD_GET(PLLDIG_RFDPHI1_MASK
, val
);
117 * If RFDPHI1 has a value of 1 the VCO frequency is also divided by
123 return DIV_ROUND_UP(data
->vco_freq
, rfdphi1
);
126 static unsigned long plldig_calc_target_div(unsigned long vco_freq
,
127 unsigned long target_rate
)
131 div
= DIV_ROUND_CLOSEST(vco_freq
, target_rate
);
132 div
= clamp(div
, 1UL, MAX_RFDPHI1
);
137 static int plldig_determine_rate(struct clk_hw
*hw
,
138 struct clk_rate_request
*req
)
140 struct clk_plldig
*data
= to_clk_plldig(hw
);
143 req
->rate
= clamp(req
->rate
, PHI1_MIN_FREQ
, PHI1_MAX_FREQ
);
144 div
= plldig_calc_target_div(data
->vco_freq
, req
->rate
);
145 req
->rate
= DIV_ROUND_UP(data
->vco_freq
, div
);
150 static int plldig_set_rate(struct clk_hw
*hw
, unsigned long rate
,
151 unsigned long parent_rate
)
153 struct clk_plldig
*data
= to_clk_plldig(hw
);
154 unsigned int val
, cond
;
155 unsigned int rfdphi1
;
157 rate
= clamp(rate
, PHI1_MIN_FREQ
, PHI1_MAX_FREQ
);
158 rfdphi1
= plldig_calc_target_div(data
->vco_freq
, rate
);
160 /* update the divider value */
161 val
= readl(data
->regs
+ PLLDIG_REG_PLLDV
);
162 val
&= ~PLLDIG_RFDPHI1_MASK
;
163 val
|= FIELD_PREP(PLLDIG_RFDPHI1_MASK
, rfdphi1
);
164 writel(val
, data
->regs
+ PLLDIG_REG_PLLDV
);
166 /* waiting for old lock state to clear */
169 /* Wait until PLL is locked or timeout */
170 return readl_poll_timeout_atomic(data
->regs
+ PLLDIG_REG_PLLSR
, cond
,
171 cond
& PLLDIG_LOCK_MASK
, 0,
175 static const struct clk_ops plldig_clk_ops
= {
176 .enable
= plldig_enable
,
177 .disable
= plldig_disable
,
178 .is_enabled
= plldig_is_enabled
,
179 .recalc_rate
= plldig_recalc_rate
,
180 .determine_rate
= plldig_determine_rate
,
181 .set_rate
= plldig_set_rate
,
184 static int plldig_init(struct clk_hw
*hw
)
186 struct clk_plldig
*data
= to_clk_plldig(hw
);
187 struct clk_hw
*parent
= clk_hw_get_parent(hw
);
188 unsigned long parent_rate
;
190 unsigned long long lltmp
;
191 unsigned int mfd
, fracdiv
= 0;
196 parent_rate
= clk_hw_get_rate(parent
);
198 if (data
->vco_freq
) {
199 mfd
= data
->vco_freq
/ parent_rate
;
200 lltmp
= data
->vco_freq
% parent_rate
;
202 do_div(lltmp
, parent_rate
);
205 mfd
= PLLDIG_DEFAULT_MFD
;
206 data
->vco_freq
= parent_rate
* mfd
;
209 val
= FIELD_PREP(PLLDIG_MFD_MASK
, mfd
);
210 writel(val
, data
->regs
+ PLLDIG_REG_PLLDV
);
212 /* Enable fractional divider */
214 val
= FIELD_PREP(PLLDIG_FRAC_MASK
, fracdiv
);
216 writel(val
, data
->regs
+ PLLDIG_REG_PLLFD
);
222 static int plldig_clk_probe(struct platform_device
*pdev
)
224 struct clk_plldig
*data
;
225 struct device
*dev
= &pdev
->dev
;
228 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
232 data
->regs
= devm_platform_ioremap_resource(pdev
, 0);
233 if (IS_ERR(data
->regs
))
234 return PTR_ERR(data
->regs
);
236 data
->hw
.init
= CLK_HW_INIT_PARENTS_DATA("dpclk",
241 ret
= devm_clk_hw_register(dev
, &data
->hw
);
243 dev_err(dev
, "failed to register %s clock\n",
248 ret
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_simple_get
,
251 dev_err(dev
, "unable to add clk provider\n");
256 * The frequency of the VCO cannot be changed during runtime.
257 * Therefore, let the user specify a desired frequency.
259 if (!of_property_read_u32(dev
->of_node
, "fsl,vco-hz",
261 if (data
->vco_freq
< PLLDIG_MIN_VCO_FREQ
||
262 data
->vco_freq
> PLLDIG_MAX_VCO_FREQ
)
266 return plldig_init(&data
->hw
);
269 static const struct of_device_id plldig_clk_id
[] = {
270 { .compatible
= "fsl,ls1028a-plldig" },
273 MODULE_DEVICE_TABLE(of
, plldig_clk_id
);
275 static struct platform_driver plldig_clk_driver
= {
277 .name
= "plldig-clock",
278 .of_match_table
= plldig_clk_id
,
280 .probe
= plldig_clk_probe
,
282 module_platform_driver(plldig_clk_driver
);
284 MODULE_LICENSE("GPL v2");
285 MODULE_AUTHOR("Wen He <wen.he_1@nxp.com>");
286 MODULE_DESCRIPTION("LS1028A Display output interface pixel clock driver");