GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / clk / meson / a1-pll.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
4  * Author: Jian Hu <jian.hu@amlogic.com>
5  *
6  * Copyright (c) 2023, SberDevices. All Rights Reserved.
7  * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
8  */
9
10 #include <linux/clk-provider.h>
11 #include <linux/mod_devicetable.h>
12 #include <linux/platform_device.h>
13 #include "a1-pll.h"
14 #include "clk-regmap.h"
15 #include "meson-clkc-utils.h"
16
17 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
18
19 static struct clk_regmap fixed_pll_dco = {
20         .data = &(struct meson_clk_pll_data){
21                 .en = {
22                         .reg_off = ANACTRL_FIXPLL_CTRL0,
23                         .shift   = 28,
24                         .width   = 1,
25                 },
26                 .m = {
27                         .reg_off = ANACTRL_FIXPLL_CTRL0,
28                         .shift   = 0,
29                         .width   = 8,
30                 },
31                 .n = {
32                         .reg_off = ANACTRL_FIXPLL_CTRL0,
33                         .shift   = 10,
34                         .width   = 5,
35                 },
36                 .frac = {
37                         .reg_off = ANACTRL_FIXPLL_CTRL1,
38                         .shift   = 0,
39                         .width   = 19,
40                 },
41                 .l = {
42                         .reg_off = ANACTRL_FIXPLL_STS,
43                         .shift   = 31,
44                         .width   = 1,
45                 },
46                 .rst = {
47                         .reg_off = ANACTRL_FIXPLL_CTRL0,
48                         .shift   = 29,
49                         .width   = 1,
50                 },
51         },
52         .hw.init = &(struct clk_init_data){
53                 .name = "fixed_pll_dco",
54                 .ops = &meson_clk_pll_ro_ops,
55                 .parent_data = &(const struct clk_parent_data) {
56                         .fw_name = "fixpll_in",
57                 },
58                 .num_parents = 1,
59         },
60 };
61
62 static struct clk_regmap fixed_pll = {
63         .data = &(struct clk_regmap_gate_data){
64                 .offset = ANACTRL_FIXPLL_CTRL0,
65                 .bit_idx = 20,
66         },
67         .hw.init = &(struct clk_init_data) {
68                 .name = "fixed_pll",
69                 .ops = &clk_regmap_gate_ops,
70                 .parent_hws = (const struct clk_hw *[]) {
71                         &fixed_pll_dco.hw
72                 },
73                 .num_parents = 1,
74         },
75 };
76
77 static const struct pll_mult_range hifi_pll_mult_range = {
78         .min = 32,
79         .max = 64,
80 };
81
82 static const struct reg_sequence hifi_init_regs[] = {
83         { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x01800000 },
84         { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00001100 },
85         { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x100a1100 },
86         { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x00302000 },
87         { .reg = ANACTRL_HIFIPLL_CTRL0, .def = 0x01f18000 },
88 };
89
90 static struct clk_regmap hifi_pll = {
91         .data = &(struct meson_clk_pll_data){
92                 .en = {
93                         .reg_off = ANACTRL_HIFIPLL_CTRL0,
94                         .shift   = 28,
95                         .width   = 1,
96                 },
97                 .m = {
98                         .reg_off = ANACTRL_HIFIPLL_CTRL0,
99                         .shift   = 0,
100                         .width   = 8,
101                 },
102                 .n = {
103                         .reg_off = ANACTRL_HIFIPLL_CTRL0,
104                         .shift   = 10,
105                         .width   = 5,
106                 },
107                 .frac = {
108                         .reg_off = ANACTRL_HIFIPLL_CTRL1,
109                         .shift   = 0,
110                         .width   = 19,
111                 },
112                 .l = {
113                         .reg_off = ANACTRL_HIFIPLL_STS,
114                         .shift   = 31,
115                         .width   = 1,
116                 },
117                 .current_en = {
118                         .reg_off = ANACTRL_HIFIPLL_CTRL0,
119                         .shift   = 26,
120                         .width   = 1,
121                 },
122                 .l_detect = {
123                         .reg_off = ANACTRL_HIFIPLL_CTRL2,
124                         .shift   = 6,
125                         .width   = 1,
126                 },
127                 .range = &hifi_pll_mult_range,
128                 .init_regs = hifi_init_regs,
129                 .init_count = ARRAY_SIZE(hifi_init_regs),
130         },
131         .hw.init = &(struct clk_init_data){
132                 .name = "hifi_pll",
133                 .ops = &meson_clk_pll_ops,
134                 .parent_data = &(const struct clk_parent_data) {
135                         .fw_name = "hifipll_in",
136                 },
137                 .num_parents = 1,
138         },
139 };
140
141 static struct clk_fixed_factor fclk_div2_div = {
142         .mult = 1,
143         .div = 2,
144         .hw.init = &(struct clk_init_data){
145                 .name = "fclk_div2_div",
146                 .ops = &clk_fixed_factor_ops,
147                 .parent_hws = (const struct clk_hw *[]) {
148                         &fixed_pll.hw
149                 },
150                 .num_parents = 1,
151         },
152 };
153
154 static struct clk_regmap fclk_div2 = {
155         .data = &(struct clk_regmap_gate_data){
156                 .offset = ANACTRL_FIXPLL_CTRL0,
157                 .bit_idx = 21,
158         },
159         .hw.init = &(struct clk_init_data){
160                 .name = "fclk_div2",
161                 .ops = &clk_regmap_gate_ops,
162                 .parent_hws = (const struct clk_hw *[]) {
163                         &fclk_div2_div.hw
164                 },
165                 .num_parents = 1,
166                 /*
167                  * This clock is used by DDR clock in BL2 firmware
168                  * and is required by the platform to operate correctly.
169                  * Until the following condition are met, we need this clock to
170                  * be marked as critical:
171                  * a) Mark the clock used by a firmware resource, if possible
172                  * b) CCF has a clock hand-off mechanism to make the sure the
173                  *    clock stays on until the proper driver comes along
174                  */
175                 .flags = CLK_IS_CRITICAL,
176         },
177 };
178
179 static struct clk_fixed_factor fclk_div3_div = {
180         .mult = 1,
181         .div = 3,
182         .hw.init = &(struct clk_init_data){
183                 .name = "fclk_div3_div",
184                 .ops = &clk_fixed_factor_ops,
185                 .parent_hws = (const struct clk_hw *[]) {
186                         &fixed_pll.hw
187                 },
188                 .num_parents = 1,
189         },
190 };
191
192 static struct clk_regmap fclk_div3 = {
193         .data = &(struct clk_regmap_gate_data){
194                 .offset = ANACTRL_FIXPLL_CTRL0,
195                 .bit_idx = 22,
196         },
197         .hw.init = &(struct clk_init_data){
198                 .name = "fclk_div3",
199                 .ops = &clk_regmap_gate_ops,
200                 .parent_hws = (const struct clk_hw *[]) {
201                         &fclk_div3_div.hw
202                 },
203                 .num_parents = 1,
204                 /*
205                  * This clock is used by APB bus which is set in boot ROM code
206                  * and is required by the platform to operate correctly.
207                  */
208                 .flags = CLK_IS_CRITICAL,
209         },
210 };
211
212 static struct clk_fixed_factor fclk_div5_div = {
213         .mult = 1,
214         .div = 5,
215         .hw.init = &(struct clk_init_data){
216                 .name = "fclk_div5_div",
217                 .ops = &clk_fixed_factor_ops,
218                 .parent_hws = (const struct clk_hw *[]) {
219                         &fixed_pll.hw
220                 },
221                 .num_parents = 1,
222         },
223 };
224
225 static struct clk_regmap fclk_div5 = {
226         .data = &(struct clk_regmap_gate_data){
227                 .offset = ANACTRL_FIXPLL_CTRL0,
228                 .bit_idx = 23,
229         },
230         .hw.init = &(struct clk_init_data){
231                 .name = "fclk_div5",
232                 .ops = &clk_regmap_gate_ops,
233                 .parent_hws = (const struct clk_hw *[]) {
234                         &fclk_div5_div.hw
235                 },
236                 .num_parents = 1,
237                 /*
238                  * This clock is used by AXI bus which setted in Romcode
239                  * and is required by the platform to operate correctly.
240                  */
241                 .flags = CLK_IS_CRITICAL,
242         },
243 };
244
245 static struct clk_fixed_factor fclk_div7_div = {
246         .mult = 1,
247         .div = 7,
248         .hw.init = &(struct clk_init_data){
249                 .name = "fclk_div7_div",
250                 .ops = &clk_fixed_factor_ops,
251                 .parent_hws = (const struct clk_hw *[]) {
252                         &fixed_pll.hw
253                 },
254                 .num_parents = 1,
255         },
256 };
257
258 static struct clk_regmap fclk_div7 = {
259         .data = &(struct clk_regmap_gate_data){
260                 .offset = ANACTRL_FIXPLL_CTRL0,
261                 .bit_idx = 24,
262         },
263         .hw.init = &(struct clk_init_data){
264                 .name = "fclk_div7",
265                 .ops = &clk_regmap_gate_ops,
266                 .parent_hws = (const struct clk_hw *[]) {
267                         &fclk_div7_div.hw
268                 },
269                 .num_parents = 1,
270         },
271 };
272
273 /* Array of all clocks registered by this provider */
274 static struct clk_hw *a1_pll_hw_clks[] = {
275         [CLKID_FIXED_PLL_DCO]   = &fixed_pll_dco.hw,
276         [CLKID_FIXED_PLL]       = &fixed_pll.hw,
277         [CLKID_FCLK_DIV2_DIV]   = &fclk_div2_div.hw,
278         [CLKID_FCLK_DIV3_DIV]   = &fclk_div3_div.hw,
279         [CLKID_FCLK_DIV5_DIV]   = &fclk_div5_div.hw,
280         [CLKID_FCLK_DIV7_DIV]   = &fclk_div7_div.hw,
281         [CLKID_FCLK_DIV2]       = &fclk_div2.hw,
282         [CLKID_FCLK_DIV3]       = &fclk_div3.hw,
283         [CLKID_FCLK_DIV5]       = &fclk_div5.hw,
284         [CLKID_FCLK_DIV7]       = &fclk_div7.hw,
285         [CLKID_HIFI_PLL]        = &hifi_pll.hw,
286 };
287
288 static struct clk_regmap *const a1_pll_regmaps[] = {
289         &fixed_pll_dco,
290         &fixed_pll,
291         &fclk_div2,
292         &fclk_div3,
293         &fclk_div5,
294         &fclk_div7,
295         &hifi_pll,
296 };
297
298 static struct regmap_config a1_pll_regmap_cfg = {
299         .reg_bits   = 32,
300         .val_bits   = 32,
301         .reg_stride = 4,
302 };
303
304 static struct meson_clk_hw_data a1_pll_clks = {
305         .hws = a1_pll_hw_clks,
306         .num = ARRAY_SIZE(a1_pll_hw_clks),
307 };
308
309 static int meson_a1_pll_probe(struct platform_device *pdev)
310 {
311         struct device *dev = &pdev->dev;
312         void __iomem *base;
313         struct regmap *map;
314         int clkid, i, err;
315
316         base = devm_platform_ioremap_resource(pdev, 0);
317         if (IS_ERR(base))
318                 return dev_err_probe(dev, PTR_ERR(base),
319                                      "can't ioremap resource\n");
320
321         map = devm_regmap_init_mmio(dev, base, &a1_pll_regmap_cfg);
322         if (IS_ERR(map))
323                 return dev_err_probe(dev, PTR_ERR(map),
324                                      "can't init regmap mmio region\n");
325
326         /* Populate regmap for the regmap backed clocks */
327         for (i = 0; i < ARRAY_SIZE(a1_pll_regmaps); i++)
328                 a1_pll_regmaps[i]->map = map;
329
330         /* Register clocks */
331         for (clkid = 0; clkid < a1_pll_clks.num; clkid++) {
332                 err = devm_clk_hw_register(dev, a1_pll_clks.hws[clkid]);
333                 if (err)
334                         return dev_err_probe(dev, err,
335                                              "clock[%d] registration failed\n",
336                                              clkid);
337         }
338
339         return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get,
340                                            &a1_pll_clks);
341 }
342
343 static const struct of_device_id a1_pll_clkc_match_table[] = {
344         { .compatible = "amlogic,a1-pll-clkc", },
345         {}
346 };
347 MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
348
349 static struct platform_driver a1_pll_clkc_driver = {
350         .probe = meson_a1_pll_probe,
351         .driver = {
352                 .name = "a1-pll-clkc",
353                 .of_match_table = a1_pll_clkc_match_table,
354         },
355 };
356
357 module_platform_driver(a1_pll_clkc_driver);
358 MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
359 MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
360 MODULE_LICENSE("GPL");