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