1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2019 NXP |
4 | * |
5 | * Clock driver for LS1028A Display output interfaces(LCD, DPHY). |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/device.h> |
10 | #include <linux/module.h> |
11 | #include <linux/err.h> |
12 | #include <linux/io.h> |
13 | #include <linux/iopoll.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/bitfield.h> |
18 | |
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 |
32 | |
33 | /* Range of the VCO frequencies, in Hz */ |
34 | #define PLLDIG_MIN_VCO_FREQ 650000000 |
35 | #define PLLDIG_MAX_VCO_FREQ 1300000000 |
36 | |
37 | /* Range of the output frequencies, in Hz */ |
38 | #define PHI1_MIN_FREQ 27000000UL |
39 | #define PHI1_MAX_FREQ 600000000UL |
40 | |
41 | /* Maximum value of the reduced frequency divider */ |
42 | #define MAX_RFDPHI1 63UL |
43 | |
44 | /* Best value of multiplication factor divider */ |
45 | #define PLLDIG_DEFAULT_MFD 44 |
46 | |
47 | /* |
48 | * Denominator part of the fractional part of the |
49 | * loop multiplication factor. |
50 | */ |
51 | #define MFDEN 20480 |
52 | |
53 | static const struct clk_parent_data parent_data[] = { |
54 | { .index = 0 }, |
55 | }; |
56 | |
57 | struct clk_plldig { |
58 | struct clk_hw hw; |
59 | void __iomem *regs; |
60 | unsigned int vco_freq; |
61 | }; |
62 | |
63 | #define to_clk_plldig(_hw) container_of(_hw, struct clk_plldig, hw) |
64 | |
65 | static int plldig_enable(struct clk_hw *hw) |
66 | { |
67 | struct clk_plldig *data = to_clk_plldig(hw); |
68 | u32 val; |
69 | |
70 | val = readl(addr: data->regs + PLLDIG_REG_PLLFM); |
71 | /* |
72 | * Use Bypass mode with PLL off by default, the frequency overshoot |
73 | * detector output was disable. SSCG Bypass mode should be enable. |
74 | */ |
75 | val |= PLLDIG_SSCGBYP_ENABLE; |
76 | writel(val, addr: data->regs + PLLDIG_REG_PLLFM); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static void plldig_disable(struct clk_hw *hw) |
82 | { |
83 | struct clk_plldig *data = to_clk_plldig(hw); |
84 | u32 val; |
85 | |
86 | val = readl(addr: data->regs + PLLDIG_REG_PLLFM); |
87 | |
88 | val &= ~PLLDIG_SSCGBYP_ENABLE; |
89 | val |= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE, 0x0); |
90 | |
91 | writel(val, addr: data->regs + PLLDIG_REG_PLLFM); |
92 | } |
93 | |
94 | static int plldig_is_enabled(struct clk_hw *hw) |
95 | { |
96 | struct clk_plldig *data = to_clk_plldig(hw); |
97 | |
98 | return readl(addr: data->regs + PLLDIG_REG_PLLFM) & |
99 | PLLDIG_SSCGBYP_ENABLE; |
100 | } |
101 | |
102 | static unsigned long plldig_recalc_rate(struct clk_hw *hw, |
103 | unsigned long parent_rate) |
104 | { |
105 | struct clk_plldig *data = to_clk_plldig(hw); |
106 | u32 val, rfdphi1; |
107 | |
108 | val = readl(addr: data->regs + PLLDIG_REG_PLLDV); |
109 | |
110 | /* Check if PLL is bypassed */ |
111 | if (val & PLLDIG_SSCGBYP_ENABLE) |
112 | return parent_rate; |
113 | |
114 | rfdphi1 = FIELD_GET(PLLDIG_RFDPHI1_MASK, val); |
115 | |
116 | /* |
117 | * If RFDPHI1 has a value of 1 the VCO frequency is also divided by |
118 | * one. |
119 | */ |
120 | if (!rfdphi1) |
121 | rfdphi1 = 1; |
122 | |
123 | return DIV_ROUND_UP(data->vco_freq, rfdphi1); |
124 | } |
125 | |
126 | static unsigned long plldig_calc_target_div(unsigned long vco_freq, |
127 | unsigned long target_rate) |
128 | { |
129 | unsigned long div; |
130 | |
131 | div = DIV_ROUND_CLOSEST(vco_freq, target_rate); |
132 | div = clamp(div, 1UL, MAX_RFDPHI1); |
133 | |
134 | return div; |
135 | } |
136 | |
137 | static int plldig_determine_rate(struct clk_hw *hw, |
138 | struct clk_rate_request *req) |
139 | { |
140 | struct clk_plldig *data = to_clk_plldig(hw); |
141 | unsigned int div; |
142 | |
143 | req->rate = clamp(req->rate, PHI1_MIN_FREQ, PHI1_MAX_FREQ); |
144 | div = plldig_calc_target_div(vco_freq: data->vco_freq, target_rate: req->rate); |
145 | req->rate = DIV_ROUND_UP(data->vco_freq, div); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int plldig_set_rate(struct clk_hw *hw, unsigned long rate, |
151 | unsigned long parent_rate) |
152 | { |
153 | struct clk_plldig *data = to_clk_plldig(hw); |
154 | unsigned int val, cond; |
155 | unsigned int rfdphi1; |
156 | |
157 | rate = clamp(rate, PHI1_MIN_FREQ, PHI1_MAX_FREQ); |
158 | rfdphi1 = plldig_calc_target_div(vco_freq: data->vco_freq, target_rate: rate); |
159 | |
160 | /* update the divider value */ |
161 | val = readl(addr: data->regs + PLLDIG_REG_PLLDV); |
162 | val &= ~PLLDIG_RFDPHI1_MASK; |
163 | val |= FIELD_PREP(PLLDIG_RFDPHI1_MASK, rfdphi1); |
164 | writel(val, addr: data->regs + PLLDIG_REG_PLLDV); |
165 | |
166 | /* waiting for old lock state to clear */ |
167 | udelay(200); |
168 | |
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, |
172 | USEC_PER_MSEC); |
173 | } |
174 | |
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, |
182 | }; |
183 | |
184 | static int plldig_init(struct clk_hw *hw) |
185 | { |
186 | struct clk_plldig *data = to_clk_plldig(hw); |
187 | struct clk_hw *parent = clk_hw_get_parent(hw); |
188 | unsigned long parent_rate; |
189 | unsigned long val; |
190 | unsigned long long lltmp; |
191 | unsigned int mfd, fracdiv = 0; |
192 | |
193 | if (!parent) |
194 | return -EINVAL; |
195 | |
196 | parent_rate = clk_hw_get_rate(hw: parent); |
197 | |
198 | if (data->vco_freq) { |
199 | mfd = data->vco_freq / parent_rate; |
200 | lltmp = data->vco_freq % parent_rate; |
201 | lltmp *= MFDEN; |
202 | do_div(lltmp, parent_rate); |
203 | fracdiv = lltmp; |
204 | } else { |
205 | mfd = PLLDIG_DEFAULT_MFD; |
206 | data->vco_freq = parent_rate * mfd; |
207 | } |
208 | |
209 | val = FIELD_PREP(PLLDIG_MFD_MASK, mfd); |
210 | writel(val, addr: data->regs + PLLDIG_REG_PLLDV); |
211 | |
212 | /* Enable fractional divider */ |
213 | if (fracdiv) { |
214 | val = FIELD_PREP(PLLDIG_FRAC_MASK, fracdiv); |
215 | val |= PLLDIG_FDEN; |
216 | writel(val, addr: data->regs + PLLDIG_REG_PLLFD); |
217 | } |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | static int plldig_clk_probe(struct platform_device *pdev) |
223 | { |
224 | struct clk_plldig *data; |
225 | struct device *dev = &pdev->dev; |
226 | int ret; |
227 | |
228 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
229 | if (!data) |
230 | return -ENOMEM; |
231 | |
232 | data->regs = devm_platform_ioremap_resource(pdev, index: 0); |
233 | if (IS_ERR(ptr: data->regs)) |
234 | return PTR_ERR(ptr: data->regs); |
235 | |
236 | data->hw.init = CLK_HW_INIT_PARENTS_DATA("dpclk" , |
237 | parent_data, |
238 | &plldig_clk_ops, |
239 | 0); |
240 | |
241 | ret = devm_clk_hw_register(dev, hw: &data->hw); |
242 | if (ret) { |
243 | dev_err(dev, "failed to register %s clock\n" , |
244 | dev->of_node->name); |
245 | return ret; |
246 | } |
247 | |
248 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_simple_get, |
249 | data: &data->hw); |
250 | if (ret) { |
251 | dev_err(dev, "unable to add clk provider\n" ); |
252 | return ret; |
253 | } |
254 | |
255 | /* |
256 | * The frequency of the VCO cannot be changed during runtime. |
257 | * Therefore, let the user specify a desired frequency. |
258 | */ |
259 | if (!of_property_read_u32(np: dev->of_node, propname: "fsl,vco-hz" , |
260 | out_value: &data->vco_freq)) { |
261 | if (data->vco_freq < PLLDIG_MIN_VCO_FREQ || |
262 | data->vco_freq > PLLDIG_MAX_VCO_FREQ) |
263 | return -EINVAL; |
264 | } |
265 | |
266 | return plldig_init(hw: &data->hw); |
267 | } |
268 | |
269 | static const struct of_device_id plldig_clk_id[] = { |
270 | { .compatible = "fsl,ls1028a-plldig" }, |
271 | { } |
272 | }; |
273 | MODULE_DEVICE_TABLE(of, plldig_clk_id); |
274 | |
275 | static struct platform_driver plldig_clk_driver = { |
276 | .driver = { |
277 | .name = "plldig-clock" , |
278 | .of_match_table = plldig_clk_id, |
279 | }, |
280 | .probe = plldig_clk_probe, |
281 | }; |
282 | module_platform_driver(plldig_clk_driver); |
283 | |
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" ); |
287 | |