Merge branches 'clk-range', 'clk-uniphier', 'clk-apple' and 'clk-qcom' into clk-next
[linux-2.6-microblaze.git] / drivers / clk / socfpga / clk-periph-s10.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2017, Intel Corporation
4  */
5 #include <linux/slab.h>
6 #include <linux/clk-provider.h>
7 #include <linux/io.h>
8
9 #include "stratix10-clk.h"
10 #include "clk.h"
11
12 #define CLK_MGR_FREE_SHIFT              16
13 #define CLK_MGR_FREE_MASK               0x7
14 #define SWCTRLBTCLKSEN_SHIFT            8
15
16 #define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
17
18 static unsigned long n5x_clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
19                                              unsigned long parent_rate)
20 {
21         struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
22         unsigned long div;
23         unsigned long shift = socfpgaclk->shift;
24         u32 val;
25
26         val = readl(socfpgaclk->hw.reg);
27         val &= (0x1f << shift);
28         div = (val >> shift) + 1;
29
30         return parent_rate / div;
31 }
32
33 static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
34                                              unsigned long parent_rate)
35 {
36         struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
37         unsigned long div = 1;
38         u32 val;
39
40         val = readl(socfpgaclk->hw.reg);
41         val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
42         parent_rate /= val;
43
44         return parent_rate / div;
45 }
46
47 static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
48                                              unsigned long parent_rate)
49 {
50         struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
51         unsigned long div = 1;
52
53         if (socfpgaclk->fixed_div) {
54                 div = socfpgaclk->fixed_div;
55         } else {
56                 if (socfpgaclk->hw.reg)
57                         div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
58         }
59
60         return parent_rate / div;
61 }
62
63 static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
64 {
65         struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
66         u32 clk_src, mask;
67         u8 parent = 0;
68
69         /* handle the bypass first */
70         if (socfpgaclk->bypass_reg) {
71                 mask = (0x1 << socfpgaclk->bypass_shift);
72                 parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
73                            socfpgaclk->bypass_shift);
74                 if (parent)
75                         return parent;
76         }
77
78         if (socfpgaclk->hw.reg) {
79                 clk_src = readl(socfpgaclk->hw.reg);
80                 parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
81                           CLK_MGR_FREE_MASK;
82         }
83         return parent;
84 }
85
86 static const struct clk_ops n5x_peri_c_clk_ops = {
87         .recalc_rate = n5x_clk_peri_c_clk_recalc_rate,
88         .get_parent = clk_periclk_get_parent,
89 };
90
91 static const struct clk_ops peri_c_clk_ops = {
92         .recalc_rate = clk_peri_c_clk_recalc_rate,
93         .get_parent = clk_periclk_get_parent,
94 };
95
96 static const struct clk_ops peri_cnt_clk_ops = {
97         .recalc_rate = clk_peri_cnt_clk_recalc_rate,
98         .get_parent = clk_periclk_get_parent,
99 };
100
101 struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,
102                                 void __iomem *reg)
103 {
104         struct clk_hw *hw_clk;
105         struct socfpga_periph_clk *periph_clk;
106         struct clk_init_data init;
107         const char *name = clks->name;
108         const char *parent_name = clks->parent_name;
109         int ret;
110
111         periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
112         if (WARN_ON(!periph_clk))
113                 return NULL;
114
115         periph_clk->hw.reg = reg + clks->offset;
116
117         init.name = name;
118         init.ops = &peri_c_clk_ops;
119         init.flags = clks->flags;
120
121         init.num_parents = clks->num_parents;
122         init.parent_names = parent_name ? &parent_name : NULL;
123         if (init.parent_names == NULL)
124                 init.parent_data = clks->parent_data;
125
126         periph_clk->hw.hw.init = &init;
127         hw_clk = &periph_clk->hw.hw;
128
129         ret = clk_hw_register(NULL, hw_clk);
130         if (ret) {
131                 kfree(periph_clk);
132                 return ERR_PTR(ret);
133         }
134         return hw_clk;
135 }
136
137 struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,
138                                 void __iomem *regbase)
139 {
140         struct clk_hw *hw_clk;
141         struct socfpga_periph_clk *periph_clk;
142         struct clk_init_data init;
143         const char *name = clks->name;
144         const char *parent_name = clks->parent_name;
145         int ret;
146
147         periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
148         if (WARN_ON(!periph_clk))
149                 return NULL;
150
151         periph_clk->hw.reg = regbase + clks->offset;
152         periph_clk->shift = clks->shift;
153
154         init.name = name;
155         init.ops = &n5x_peri_c_clk_ops;
156         init.flags = clks->flags;
157
158         init.num_parents = clks->num_parents;
159         init.parent_names = parent_name ? &parent_name : NULL;
160
161         periph_clk->hw.hw.init = &init;
162         hw_clk = &periph_clk->hw.hw;
163
164         ret = clk_hw_register(NULL, hw_clk);
165         if (ret) {
166                 kfree(periph_clk);
167                 return ERR_PTR(ret);
168         }
169         return hw_clk;
170 }
171
172 struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
173                                     void __iomem *regbase)
174 {
175         struct clk_hw *hw_clk;
176         struct socfpga_periph_clk *periph_clk;
177         struct clk_init_data init;
178         const char *name = clks->name;
179         const char *parent_name = clks->parent_name;
180         int ret;
181
182         periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
183         if (WARN_ON(!periph_clk))
184                 return NULL;
185
186         if (clks->offset)
187                 periph_clk->hw.reg = regbase + clks->offset;
188         else
189                 periph_clk->hw.reg = NULL;
190
191         if (clks->bypass_reg)
192                 periph_clk->bypass_reg = regbase + clks->bypass_reg;
193         else
194                 periph_clk->bypass_reg = NULL;
195         periph_clk->bypass_shift = clks->bypass_shift;
196         periph_clk->fixed_div = clks->fixed_divider;
197
198         init.name = name;
199         init.ops = &peri_cnt_clk_ops;
200         init.flags = clks->flags;
201
202         init.num_parents = clks->num_parents;
203         init.parent_names = parent_name ? &parent_name : NULL;
204         if (init.parent_names == NULL)
205                 init.parent_data = clks->parent_data;
206
207         periph_clk->hw.hw.init = &init;
208         hw_clk = &periph_clk->hw.hw;
209
210         ret = clk_hw_register(NULL, hw_clk);
211         if (ret) {
212                 kfree(periph_clk);
213                 return ERR_PTR(ret);
214         }
215         return hw_clk;
216 }