]> Git Repo - linux.git/blob - drivers/clk/clk-loongson2.c
x86/kaslr: Expose and use the end of the physical memory address space
[linux.git] / drivers / clk / clk-loongson2.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Author: Yinbo Zhu <[email protected]>
4  * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
5  */
6
7 #include <linux/err.h>
8 #include <linux/init.h>
9 #include <linux/clk-provider.h>
10 #include <linux/slab.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/io-64-nonatomic-lo-hi.h>
14 #include <dt-bindings/clock/loongson,ls2k-clk.h>
15
16 static const struct clk_parent_data pdata[] = {
17         { .fw_name = "ref_100m", },
18 };
19
20 enum loongson2_clk_type {
21         CLK_TYPE_PLL,
22         CLK_TYPE_SCALE,
23         CLK_TYPE_DIVIDER,
24         CLK_TYPE_GATE,
25         CLK_TYPE_FIXED,
26         CLK_TYPE_NONE,
27 };
28
29 struct loongson2_clk_provider {
30         void __iomem *base;
31         struct device *dev;
32         struct clk_hw_onecell_data clk_data;
33         spinlock_t clk_lock;    /* protect access to DIV registers */
34 };
35
36 struct loongson2_clk_data {
37         struct clk_hw hw;
38         void __iomem *reg;
39         u8 div_shift;
40         u8 div_width;
41         u8 mult_shift;
42         u8 mult_width;
43 };
44
45 struct loongson2_clk_board_info {
46         u8 id;
47         enum loongson2_clk_type type;
48         const char *name;
49         const char *parent_name;
50         unsigned long fixed_rate;
51         u8 reg_offset;
52         u8 div_shift;
53         u8 div_width;
54         u8 mult_shift;
55         u8 mult_width;
56         u8 bit_idx;
57 };
58
59 #define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth)  \
60         {                                                       \
61                 .id             = _id,                          \
62                 .type           = CLK_TYPE_DIVIDER,             \
63                 .name           = _name,                        \
64                 .parent_name    = _pname,                       \
65                 .reg_offset     = _offset,                      \
66                 .div_shift      = _dshift,                      \
67                 .div_width      = _dwidth,                      \
68         }
69
70 #define CLK_PLL(_id, _name, _offset, _mshift, _mwidth,          \
71                 _dshift, _dwidth)                               \
72         {                                                       \
73                 .id             = _id,                          \
74                 .type           = CLK_TYPE_PLL,                 \
75                 .name           = _name,                        \
76                 .parent_name    = NULL,                         \
77                 .reg_offset     = _offset,                      \
78                 .mult_shift     = _mshift,                      \
79                 .mult_width     = _mwidth,                      \
80                 .div_shift      = _dshift,                      \
81                 .div_width      = _dwidth,                      \
82         }
83
84 #define CLK_SCALE(_id, _name, _pname, _offset,                  \
85                   _dshift, _dwidth)                             \
86         {                                                       \
87                 .id             = _id,                          \
88                 .type           = CLK_TYPE_SCALE,               \
89                 .name           = _name,                        \
90                 .parent_name    = _pname,                       \
91                 .reg_offset     = _offset,                      \
92                 .div_shift      = _dshift,                      \
93                 .div_width      = _dwidth,                      \
94         }
95
96 #define CLK_GATE(_id, _name, _pname, _offset, _bidx)            \
97         {                                                       \
98                 .id             = _id,                          \
99                 .type           = CLK_TYPE_GATE,                \
100                 .name           = _name,                        \
101                 .parent_name    = _pname,                       \
102                 .reg_offset     = _offset,                      \
103                 .bit_idx        = _bidx,                        \
104         }
105
106 #define CLK_FIXED(_id, _name, _pname, _rate)                    \
107         {                                                       \
108                 .id             = _id,                          \
109                 .type           = CLK_TYPE_FIXED,               \
110                 .name           = _name,                        \
111                 .parent_name    = _pname,                       \
112                 .fixed_rate     = _rate,                        \
113         }
114
115 static const struct loongson2_clk_board_info ls2k0500_clks[] = {
116         CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    16, 8, 8, 6),
117         CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x8,  16, 8, 8, 6),
118         CLK_PLL(LOONGSON2_DC_PLL,     "pll_soc",  0x10, 16, 8, 8, 6),
119         CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x18, 16, 8, 8, 6),
120         CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x20, 16, 8, 8, 6),
121         CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0,    24, 6),
122         CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x8,  24, 6),
123         CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0xc,  8,  6),
124         CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_soc",  0x10, 24, 6),
125         CLK_DIV(LOONGSON2_DC_CLK,     "clk_sb",   "pll_soc",  0x14, 0,  6),
126         CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_soc",  0x14, 8,  6),
127         CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x18, 24, 6),
128         CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x20, 24, 6),
129         CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb",   0x28, 8,  3),
130         CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb",   0x28, 12, 3),
131         CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_sb",   0x28, 16, 3),
132         CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_sb",   0x28, 20, 3),
133         { /* Sentinel */ },
134 };
135
136 static const struct loongson2_clk_board_info ls2k1000_clks[] = {
137         CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    32, 10, 26, 6),
138         CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x10, 32, 10, 26, 6),
139         CLK_PLL(LOONGSON2_DC_PLL,     "pll_dc",   0x20, 32, 10, 26, 6),
140         CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 32, 10, 26, 6),
141         CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 32, 10, 26, 6),
142         CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0x8,  0,  6),
143         CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x18, 0,  6),
144         CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_ddr",  0x18, 22, 6),
145         /*
146          * The hda clk divisor in the upper 32bits and the clk-prodiver
147          * layer code doesn't support 64bit io operation thus a conversion
148          * is required that subtract shift by 32 and add 4byte to the hda
149          * address
150          */
151         CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0x22, 12, 7),
152         CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "pll_dc",   0x28, 0,  6),
153         CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_dc",   0x28, 22, 6),
154         CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x38, 0,  6),
155         CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x38, 0,  6),
156         CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,       0x50, 8,  3),
157         CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
158         CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_gmac", 0x50, 16, 3),
159         CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_gmac", 0x50, 20, 3),
160         { /* Sentinel */ },
161 };
162
163 static const struct loongson2_clk_board_info ls2k2000_clks[] = {
164         CLK_PLL(LOONGSON2_DC_PLL,     "pll_0",    0,    21, 9, 32, 6),
165         CLK_PLL(LOONGSON2_DDR_PLL,    "pll_1",    0x10, 21, 9, 32, 6),
166         CLK_PLL(LOONGSON2_NODE_PLL,   "pll_2",    0x20, 21, 9, 32, 6),
167         CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 21, 9, 32, 6),
168         CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 21, 9, 32, 6),
169         CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0",    0,    40),
170         CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0",    0,    41),
171         CLK_GATE(LOONGSON2_RIO_GATE,  "rio_gate",  "pll_0",    0,    42),
172         CLK_GATE(LOONGSON2_DC_GATE,   "dc_gate",   "pll_1",    0x10, 40),
173         CLK_GATE(LOONGSON2_DDR_GATE,  "ddr_gate",  "pll_1",    0x10, 41),
174         CLK_GATE(LOONGSON2_GPU_GATE,  "gpu_gate",  "pll_1",    0x10, 42),
175         CLK_GATE(LOONGSON2_HDA_GATE,  "hda_gate",  "pll_2",    0x20, 40),
176         CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2",    0x20, 41),
177         CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2",    0x20, 42),
178         CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
179         CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
180         CLK_DIV(LOONGSON2_OUT0_CLK,   "clk_out0", "out0_gate", 0,    0,  6),
181         CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "gmac_gate", 0,    7,  6),
182         CLK_DIV(LOONGSON2_RIO_CLK,    "clk_rio",  "rio_gate",  0,    14, 6),
183         CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "dc_gate",   0x10, 0,  6),
184         CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "gpu_gate",  0x10, 7,  6),
185         CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "ddr_gate",  0x10, 14, 6),
186         CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "hda_gate",  0x20, 0,  6),
187         CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "node_gate", 0x20, 7,  6),
188         CLK_DIV(LOONGSON2_EMMC_CLK,   "clk_emmc", "emmc_gate", 0x20, 14, 6),
189         CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0",  0x30, 0,  6),
190         CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1",  0x40, 0,  6),
191         CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0",  0x50, 12, 3),
192         CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_out0",  0x50, 16, 3),
193         CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_node",  0x50, 20, 3),
194         CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,        0x50, 23, 3),
195         CLK_SCALE(LOONGSON2_DES_CLK,  "clk_des",  "clk_node",  0x50, 40, 3),
196         CLK_SCALE(LOONGSON2_I2S_CLK,  "clk_i2s",  "clk_node",  0x50, 44, 3),
197         CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
198         { /* Sentinel */ },
199 };
200
201 static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
202 {
203         return container_of(hw, struct loongson2_clk_data, hw);
204 }
205
206 static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
207 {
208         return (val & GENMASK(shift + width - 1, shift)) >> shift;
209 }
210
211 static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
212                                                unsigned long parent_rate)
213 {
214         u64 val, mult, div;
215         struct loongson2_clk_data *clk = to_loongson2_clk(hw);
216
217         val  = readq(clk->reg);
218         mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
219         div  = loongson2_rate_part(val, clk->div_shift,  clk->div_width);
220
221         return div_u64((u64)parent_rate * mult, div);
222 }
223
224 static const struct clk_ops loongson2_pll_recalc_ops = {
225         .recalc_rate = loongson2_pll_recalc_rate,
226 };
227
228 static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
229                                                      unsigned long parent_rate)
230 {
231         u64 val, mult;
232         struct loongson2_clk_data *clk = to_loongson2_clk(hw);
233
234         val  = readq(clk->reg);
235         mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
236
237         return div_u64((u64)parent_rate * mult, 8);
238 }
239
240 static const struct clk_ops loongson2_freqscale_recalc_ops = {
241         .recalc_rate = loongson2_freqscale_recalc_rate,
242 };
243
244 static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp,
245                                              const struct loongson2_clk_board_info *cld,
246                                              const struct clk_ops *ops)
247 {
248         int ret;
249         struct clk_hw *hw;
250         struct loongson2_clk_data *clk;
251         struct clk_init_data init = { };
252
253         clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
254         if (!clk)
255                 return ERR_PTR(-ENOMEM);
256
257         init.name  = cld->name;
258         init.ops   = ops;
259         init.flags = 0;
260         init.num_parents = 1;
261
262         if (!cld->parent_name)
263                 init.parent_data = pdata;
264         else
265                 init.parent_names = &cld->parent_name;
266
267         clk->reg        = clp->base + cld->reg_offset;
268         clk->div_shift  = cld->div_shift;
269         clk->div_width  = cld->div_width;
270         clk->mult_shift = cld->mult_shift;
271         clk->mult_width = cld->mult_width;
272         clk->hw.init    = &init;
273
274         hw = &clk->hw;
275         ret = devm_clk_hw_register(clp->dev, hw);
276         if (ret)
277                 clk = ERR_PTR(ret);
278
279         return hw;
280 }
281
282 static int loongson2_clk_probe(struct platform_device *pdev)
283 {
284         int i, clks_num = 0;
285         struct clk_hw *hw;
286         struct device *dev = &pdev->dev;
287         struct loongson2_clk_provider *clp;
288         const struct loongson2_clk_board_info *p, *data;
289
290         data = device_get_match_data(dev);
291         if (!data)
292                 return -EINVAL;
293
294         for (p = data; p->name; p++)
295                 clks_num++;
296
297         clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
298                            GFP_KERNEL);
299         if (!clp)
300                 return -ENOMEM;
301
302         clp->base = devm_platform_ioremap_resource(pdev, 0);
303         if (IS_ERR(clp->base))
304                 return PTR_ERR(clp->base);
305
306         spin_lock_init(&clp->clk_lock);
307         clp->clk_data.num = clks_num + 1;
308         clp->dev = dev;
309
310         for (i = 0; i < clks_num; i++) {
311                 p = &data[i];
312                 switch (p->type) {
313                 case CLK_TYPE_PLL:
314                         hw = loongson2_clk_register(clp, p,
315                                                     &loongson2_pll_recalc_ops);
316                         break;
317                 case CLK_TYPE_SCALE:
318                         hw = loongson2_clk_register(clp, p,
319                                                     &loongson2_freqscale_recalc_ops);
320                         break;
321                 case CLK_TYPE_DIVIDER:
322                         hw = devm_clk_hw_register_divider(dev, p->name,
323                                                           p->parent_name, 0,
324                                                           clp->base + p->reg_offset,
325                                                           p->div_shift, p->div_width,
326                                                           CLK_DIVIDER_ONE_BASED,
327                                                           &clp->clk_lock);
328                         break;
329                 case CLK_TYPE_GATE:
330                         hw = devm_clk_hw_register_gate(dev, p->name, p->parent_name, 0,
331                                                        clp->base + p->reg_offset,
332                                                        p->bit_idx, 0,
333                                                        &clp->clk_lock);
334                         break;
335                 case CLK_TYPE_FIXED:
336                         hw = clk_hw_register_fixed_rate_parent_data(dev, p->name, pdata,
337                                                                     0, p->fixed_rate);
338                         break;
339                 default:
340                         return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
341                 }
342
343                 if (IS_ERR(hw))
344                         return dev_err_probe(dev, PTR_ERR(hw),
345                                              "Register clk: %s, type: %u failed!\n",
346                                              p->name, p->type);
347
348                 clp->clk_data.hws[p->id] = hw;
349         }
350
351         return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
352 }
353
354 static const struct of_device_id loongson2_clk_match_table[] = {
355         { .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
356         { .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
357         { .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
358         { }
359 };
360 MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
361
362 static struct platform_driver loongson2_clk_driver = {
363         .probe  = loongson2_clk_probe,
364         .driver = {
365                 .name   = "loongson2-clk",
366                 .of_match_table = loongson2_clk_match_table,
367         },
368 };
369 module_platform_driver(loongson2_clk_driver);
370
371 MODULE_DESCRIPTION("Loongson2 clock driver");
372 MODULE_AUTHOR("Loongson Technology Corporation Limited");
373 MODULE_LICENSE("GPL");
This page took 0.053124 seconds and 4 git commands to generate.