]>
Commit | Line | Data |
---|---|---|
ff5f87cb HG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Clock driver for TPS68470 PMIC | |
4 | * | |
5 | * Copyright (c) 2021 Red Hat Inc. | |
6 | * Copyright (C) 2018 Intel Corporation | |
7 | * | |
8 | * Authors: | |
9 | * Hans de Goede <[email protected]> | |
10 | * Zaikuo Wang <[email protected]> | |
11 | * Tianshu Qiu <[email protected]> | |
12 | * Jian Xu Zheng <[email protected]> | |
13 | * Yuning Pu <[email protected]> | |
14 | * Antti Laakso <[email protected]> | |
15 | */ | |
16 | ||
17 | #include <linux/clk-provider.h> | |
18 | #include <linux/clkdev.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/mfd/tps68470.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/platform_data/tps68470.h> | |
24 | #include <linux/regmap.h> | |
25 | ||
26 | #define TPS68470_CLK_NAME "tps68470-clk" | |
27 | ||
28 | #define to_tps68470_clkdata(clkd) \ | |
29 | container_of(clkd, struct tps68470_clkdata, clkout_hw) | |
30 | ||
31 | static struct tps68470_clkout_freqs { | |
32 | unsigned long freq; | |
33 | unsigned int xtaldiv; | |
34 | unsigned int plldiv; | |
35 | unsigned int postdiv; | |
36 | unsigned int buckdiv; | |
37 | unsigned int boostdiv; | |
38 | } clk_freqs[] = { | |
39 | /* | |
40 | * The PLL is used to multiply the crystal oscillator | |
41 | * frequency range of 3 MHz to 27 MHz by a programmable | |
42 | * factor of F = (M/N)*(1/P) such that the output | |
43 | * available at the HCLK_A or HCLK_B pins are in the range | |
44 | * of 4 MHz to 64 MHz in increments of 0.1 MHz. | |
45 | * | |
46 | * hclk_# = osc_in * (((plldiv*2)+320) / (xtaldiv+30)) * (1 / 2^postdiv) | |
47 | * | |
48 | * PLL_REF_CLK should be as close as possible to 100kHz | |
49 | * PLL_REF_CLK = input clk / XTALDIV[7:0] + 30) | |
50 | * | |
51 | * PLL_VCO_CLK = (PLL_REF_CLK * (plldiv*2 + 320)) | |
52 | * | |
53 | * BOOST should be as close as possible to 2Mhz | |
54 | * BOOST = PLL_VCO_CLK / (BOOSTDIV[4:0] + 16) * | |
55 | * | |
56 | * BUCK should be as close as possible to 5.2Mhz | |
57 | * BUCK = PLL_VCO_CLK / (BUCKDIV[3:0] + 5) | |
58 | * | |
59 | * osc_in xtaldiv plldiv postdiv hclk_# | |
60 | * 20Mhz 170 32 1 19.2Mhz | |
61 | * 20Mhz 170 40 1 20Mhz | |
62 | * 20Mhz 170 80 1 24Mhz | |
63 | */ | |
64 | { 19200000, 170, 32, 1, 2, 3 }, | |
65 | { 20000000, 170, 40, 1, 3, 4 }, | |
66 | { 24000000, 170, 80, 1, 4, 8 }, | |
67 | }; | |
68 | ||
69 | struct tps68470_clkdata { | |
70 | struct clk_hw clkout_hw; | |
71 | struct regmap *regmap; | |
72 | unsigned long rate; | |
73 | }; | |
74 | ||
75 | static int tps68470_clk_is_prepared(struct clk_hw *hw) | |
76 | { | |
77 | struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); | |
78 | int val; | |
79 | ||
80 | if (regmap_read(clkdata->regmap, TPS68470_REG_PLLCTL, &val)) | |
81 | return 0; | |
82 | ||
83 | return val & TPS68470_PLL_EN_MASK; | |
84 | } | |
85 | ||
86 | static int tps68470_clk_prepare(struct clk_hw *hw) | |
87 | { | |
88 | struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); | |
89 | ||
90 | regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG1, | |
91 | (TPS68470_PLL_OUTPUT_ENABLE << TPS68470_OUTPUT_A_SHIFT) | | |
92 | (TPS68470_PLL_OUTPUT_ENABLE << TPS68470_OUTPUT_B_SHIFT)); | |
93 | ||
94 | regmap_update_bits(clkdata->regmap, TPS68470_REG_PLLCTL, | |
95 | TPS68470_PLL_EN_MASK, TPS68470_PLL_EN_MASK); | |
96 | ||
97 | /* | |
98 | * The PLLCTL reg lock bit is set by the PMIC after approx. 4ms and | |
99 | * does not indicate a true lock, so just wait 4 ms. | |
100 | */ | |
101 | usleep_range(4000, 5000); | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static void tps68470_clk_unprepare(struct clk_hw *hw) | |
107 | { | |
108 | struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); | |
109 | ||
110 | /* Disable clock first ... */ | |
111 | regmap_update_bits(clkdata->regmap, TPS68470_REG_PLLCTL, TPS68470_PLL_EN_MASK, 0); | |
112 | ||
113 | /* ... and then tri-state the clock outputs. */ | |
114 | regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG1, 0); | |
115 | } | |
116 | ||
117 | static unsigned long tps68470_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | |
118 | { | |
119 | struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); | |
120 | ||
121 | return clkdata->rate; | |
122 | } | |
123 | ||
124 | /* | |
125 | * This returns the index of the clk_freqs[] cfg with the closest rate for | |
126 | * use in tps68470_clk_round_rate(). tps68470_clk_set_rate() checks that | |
127 | * the rate of the returned cfg is an exact match. | |
128 | */ | |
129 | static unsigned int tps68470_clk_cfg_lookup(unsigned long rate) | |
130 | { | |
131 | long diff, best_diff = LONG_MAX; | |
132 | unsigned int i, best_idx = 0; | |
133 | ||
134 | for (i = 0; i < ARRAY_SIZE(clk_freqs); i++) { | |
135 | diff = clk_freqs[i].freq - rate; | |
136 | if (diff == 0) | |
137 | return i; | |
138 | ||
139 | diff = abs(diff); | |
140 | if (diff < best_diff) { | |
141 | best_diff = diff; | |
142 | best_idx = i; | |
143 | } | |
144 | } | |
145 | ||
146 | return best_idx; | |
147 | } | |
148 | ||
149 | static long tps68470_clk_round_rate(struct clk_hw *hw, unsigned long rate, | |
150 | unsigned long *parent_rate) | |
151 | { | |
152 | unsigned int idx = tps68470_clk_cfg_lookup(rate); | |
153 | ||
154 | return clk_freqs[idx].freq; | |
155 | } | |
156 | ||
157 | static int tps68470_clk_set_rate(struct clk_hw *hw, unsigned long rate, | |
158 | unsigned long parent_rate) | |
159 | { | |
160 | struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); | |
161 | unsigned int idx = tps68470_clk_cfg_lookup(rate); | |
162 | ||
163 | if (rate != clk_freqs[idx].freq) | |
164 | return -EINVAL; | |
165 | ||
166 | regmap_write(clkdata->regmap, TPS68470_REG_BOOSTDIV, clk_freqs[idx].boostdiv); | |
167 | regmap_write(clkdata->regmap, TPS68470_REG_BUCKDIV, clk_freqs[idx].buckdiv); | |
168 | regmap_write(clkdata->regmap, TPS68470_REG_PLLSWR, TPS68470_PLLSWR_DEFAULT); | |
169 | regmap_write(clkdata->regmap, TPS68470_REG_XTALDIV, clk_freqs[idx].xtaldiv); | |
170 | regmap_write(clkdata->regmap, TPS68470_REG_PLLDIV, clk_freqs[idx].plldiv); | |
171 | regmap_write(clkdata->regmap, TPS68470_REG_POSTDIV, clk_freqs[idx].postdiv); | |
172 | regmap_write(clkdata->regmap, TPS68470_REG_POSTDIV2, clk_freqs[idx].postdiv); | |
173 | regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG2, TPS68470_CLKCFG2_DRV_STR_2MA); | |
174 | ||
175 | regmap_write(clkdata->regmap, TPS68470_REG_PLLCTL, | |
176 | TPS68470_OSC_EXT_CAP_DEFAULT << TPS68470_OSC_EXT_CAP_SHIFT | | |
177 | TPS68470_CLK_SRC_XTAL << TPS68470_CLK_SRC_SHIFT); | |
178 | ||
179 | clkdata->rate = rate; | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static const struct clk_ops tps68470_clk_ops = { | |
185 | .is_prepared = tps68470_clk_is_prepared, | |
186 | .prepare = tps68470_clk_prepare, | |
187 | .unprepare = tps68470_clk_unprepare, | |
188 | .recalc_rate = tps68470_clk_recalc_rate, | |
189 | .round_rate = tps68470_clk_round_rate, | |
190 | .set_rate = tps68470_clk_set_rate, | |
191 | }; | |
192 | ||
193 | static int tps68470_clk_probe(struct platform_device *pdev) | |
194 | { | |
195 | struct tps68470_clk_platform_data *pdata = pdev->dev.platform_data; | |
196 | struct clk_init_data tps68470_clk_initdata = { | |
197 | .name = TPS68470_CLK_NAME, | |
198 | .ops = &tps68470_clk_ops, | |
199 | /* Changing the dividers when the PLL is on is not allowed */ | |
200 | .flags = CLK_SET_RATE_GATE, | |
201 | }; | |
202 | struct tps68470_clkdata *tps68470_clkdata; | |
43cf3697 | 203 | struct tps68470_clk_consumer *consumer; |
ff5f87cb | 204 | int ret; |
43cf3697 | 205 | int i; |
ff5f87cb HG |
206 | |
207 | tps68470_clkdata = devm_kzalloc(&pdev->dev, sizeof(*tps68470_clkdata), | |
208 | GFP_KERNEL); | |
209 | if (!tps68470_clkdata) | |
210 | return -ENOMEM; | |
211 | ||
212 | tps68470_clkdata->regmap = dev_get_drvdata(pdev->dev.parent); | |
213 | tps68470_clkdata->clkout_hw.init = &tps68470_clk_initdata; | |
214 | ||
215 | /* Set initial rate */ | |
216 | tps68470_clk_set_rate(&tps68470_clkdata->clkout_hw, clk_freqs[0].freq, 0); | |
217 | ||
218 | ret = devm_clk_hw_register(&pdev->dev, &tps68470_clkdata->clkout_hw); | |
219 | if (ret) | |
220 | return ret; | |
221 | ||
222 | ret = devm_clk_hw_register_clkdev(&pdev->dev, &tps68470_clkdata->clkout_hw, | |
223 | TPS68470_CLK_NAME, NULL); | |
224 | if (ret) | |
225 | return ret; | |
226 | ||
227 | if (pdata) { | |
43cf3697 DS |
228 | for (i = 0; i < pdata->n_consumers; i++) { |
229 | consumer = &pdata->consumers[i]; | |
230 | ret = devm_clk_hw_register_clkdev(&pdev->dev, | |
231 | &tps68470_clkdata->clkout_hw, | |
232 | consumer->consumer_con_id, | |
233 | consumer->consumer_dev_name); | |
234 | } | |
ff5f87cb HG |
235 | } |
236 | ||
237 | return ret; | |
238 | } | |
239 | ||
240 | static struct platform_driver tps68470_clk_driver = { | |
241 | .driver = { | |
242 | .name = TPS68470_CLK_NAME, | |
243 | }, | |
244 | .probe = tps68470_clk_probe, | |
245 | }; | |
246 | ||
247 | /* | |
248 | * The ACPI tps68470 probe-ordering depends on the clk/gpio/regulator drivers | |
249 | * registering before the drivers for the camera-sensors which use them bind. | |
250 | * subsys_initcall() ensures this when the drivers are builtin. | |
251 | */ | |
252 | static int __init tps68470_clk_init(void) | |
253 | { | |
254 | return platform_driver_register(&tps68470_clk_driver); | |
255 | } | |
256 | subsys_initcall(tps68470_clk_init); | |
257 | ||
258 | static void __exit tps68470_clk_exit(void) | |
259 | { | |
260 | platform_driver_unregister(&tps68470_clk_driver); | |
261 | } | |
262 | module_exit(tps68470_clk_exit); | |
263 | ||
264 | MODULE_ALIAS("platform:tps68470-clk"); | |
265 | MODULE_DESCRIPTION("clock driver for TPS68470 pmic"); | |
266 | MODULE_LICENSE("GPL"); |