GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / clk / socfpga / clk-gate-s10.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2017, Intel Corporation
4  */
5 #include <linux/clk-provider.h>
6 #include <linux/io.h>
7 #include <linux/slab.h>
8 #include "stratix10-clk.h"
9 #include "clk.h"
10
11 #define SOCFPGA_CS_PDBG_CLK     "cs_pdbg_clk"
12 #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
13
14 #define SOCFPGA_EMAC0_CLK               "emac0_clk"
15 #define SOCFPGA_EMAC1_CLK               "emac1_clk"
16 #define SOCFPGA_EMAC2_CLK               "emac2_clk"
17 #define AGILEX_BYPASS_OFFSET            0xC
18 #define STRATIX10_BYPASS_OFFSET         0x2C
19 #define BOOTCLK_BYPASS                  2
20
21 static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
22                                                   unsigned long parent_rate)
23 {
24         struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
25         u32 div = 1, val;
26
27         if (socfpgaclk->fixed_div) {
28                 div = socfpgaclk->fixed_div;
29         } else if (socfpgaclk->div_reg) {
30                 val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
31                 val &= GENMASK(socfpgaclk->width - 1, 0);
32                 div = (1 << val);
33         }
34         return parent_rate / div;
35 }
36
37 static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk,
38                                                   unsigned long parent_rate)
39 {
40         struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
41         u32 div, val;
42
43         val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
44         val &= GENMASK(socfpgaclk->width - 1, 0);
45         div = (1 << val);
46         div = div ? 4 : 1;
47
48         return parent_rate / div;
49 }
50
51 static u8 socfpga_gate_get_parent(struct clk_hw *hwclk)
52 {
53         struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
54         u32 mask, second_bypass;
55         u8 parent = 0;
56         const char *name = clk_hw_get_name(hwclk);
57
58         if (socfpgaclk->bypass_reg) {
59                 mask = (0x1 << socfpgaclk->bypass_shift);
60                 parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
61                           socfpgaclk->bypass_shift);
62         }
63
64         if (streq(name, SOCFPGA_EMAC0_CLK) ||
65             streq(name, SOCFPGA_EMAC1_CLK) ||
66             streq(name, SOCFPGA_EMAC2_CLK)) {
67                 second_bypass = readl(socfpgaclk->bypass_reg -
68                                       STRATIX10_BYPASS_OFFSET);
69                 /* EMACA bypass to bootclk @0xB0 offset */
70                 if (second_bypass & 0x1)
71                         if (parent == 0) /* only applicable if parent is maca */
72                                 parent = BOOTCLK_BYPASS;
73
74                 if (second_bypass & 0x2)
75                         if (parent == 1) /* only applicable if parent is macb */
76                                 parent = BOOTCLK_BYPASS;
77         }
78         return parent;
79 }
80
81 static u8 socfpga_agilex_gate_get_parent(struct clk_hw *hwclk)
82 {
83         struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
84         u32 mask, second_bypass;
85         u8 parent = 0;
86         const char *name = clk_hw_get_name(hwclk);
87
88         if (socfpgaclk->bypass_reg) {
89                 mask = (0x1 << socfpgaclk->bypass_shift);
90                 parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
91                           socfpgaclk->bypass_shift);
92         }
93
94         if (streq(name, SOCFPGA_EMAC0_CLK) ||
95             streq(name, SOCFPGA_EMAC1_CLK) ||
96             streq(name, SOCFPGA_EMAC2_CLK)) {
97                 second_bypass = readl(socfpgaclk->bypass_reg -
98                                       AGILEX_BYPASS_OFFSET);
99                 /* EMACA bypass to bootclk @0x88 offset */
100                 if (second_bypass & 0x1)
101                         if (parent == 0) /* only applicable if parent is maca */
102                                 parent = BOOTCLK_BYPASS;
103
104                 if (second_bypass & 0x2)
105                         if (parent == 1) /* only applicable if parent is macb */
106                                 parent = BOOTCLK_BYPASS;
107         }
108
109         return parent;
110 }
111
112 static struct clk_ops gateclk_ops = {
113         .recalc_rate = socfpga_gate_clk_recalc_rate,
114         .get_parent = socfpga_gate_get_parent,
115 };
116
117 static const struct clk_ops agilex_gateclk_ops = {
118         .recalc_rate = socfpga_gate_clk_recalc_rate,
119         .get_parent = socfpga_agilex_gate_get_parent,
120 };
121
122 static const struct clk_ops dbgclk_ops = {
123         .recalc_rate = socfpga_dbg_clk_recalc_rate,
124         .get_parent = socfpga_gate_get_parent,
125 };
126
127 struct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
128 {
129         struct clk_hw *hw_clk;
130         struct socfpga_gate_clk *socfpga_clk;
131         struct clk_init_data init;
132         const char *parent_name = clks->parent_name;
133         int ret;
134
135         socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
136         if (!socfpga_clk)
137                 return NULL;
138
139         socfpga_clk->hw.reg = regbase + clks->gate_reg;
140         socfpga_clk->hw.bit_idx = clks->gate_idx;
141
142         gateclk_ops.enable = clk_gate_ops.enable;
143         gateclk_ops.disable = clk_gate_ops.disable;
144
145         socfpga_clk->fixed_div = clks->fixed_div;
146
147         if (clks->div_reg)
148                 socfpga_clk->div_reg = regbase + clks->div_reg;
149         else
150                 socfpga_clk->div_reg = NULL;
151
152         socfpga_clk->width = clks->div_width;
153         socfpga_clk->shift = clks->div_offset;
154
155         if (clks->bypass_reg)
156                 socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
157         else
158                 socfpga_clk->bypass_reg = NULL;
159         socfpga_clk->bypass_shift = clks->bypass_shift;
160
161         if (streq(clks->name, "cs_pdbg_clk"))
162                 init.ops = &dbgclk_ops;
163         else
164                 init.ops = &gateclk_ops;
165
166         init.name = clks->name;
167         init.flags = clks->flags;
168
169         init.num_parents = clks->num_parents;
170         init.parent_names = parent_name ? &parent_name : NULL;
171         if (init.parent_names == NULL)
172                 init.parent_data = clks->parent_data;
173         socfpga_clk->hw.hw.init = &init;
174
175         hw_clk = &socfpga_clk->hw.hw;
176
177         ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
178         if (ret) {
179                 kfree(socfpga_clk);
180                 return ERR_PTR(ret);
181         }
182         return hw_clk;
183 }
184
185 struct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
186 {
187         struct clk_hw *hw_clk;
188         struct socfpga_gate_clk *socfpga_clk;
189         struct clk_init_data init;
190         const char *parent_name = clks->parent_name;
191         int ret;
192
193         socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
194         if (!socfpga_clk)
195                 return NULL;
196
197         socfpga_clk->hw.reg = regbase + clks->gate_reg;
198         socfpga_clk->hw.bit_idx = clks->gate_idx;
199
200         gateclk_ops.enable = clk_gate_ops.enable;
201         gateclk_ops.disable = clk_gate_ops.disable;
202
203         socfpga_clk->fixed_div = clks->fixed_div;
204
205         if (clks->div_reg)
206                 socfpga_clk->div_reg = regbase + clks->div_reg;
207         else
208                 socfpga_clk->div_reg = NULL;
209
210         socfpga_clk->width = clks->div_width;
211         socfpga_clk->shift = clks->div_offset;
212
213         if (clks->bypass_reg)
214                 socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
215         else
216                 socfpga_clk->bypass_reg = NULL;
217         socfpga_clk->bypass_shift = clks->bypass_shift;
218
219         if (streq(clks->name, "cs_pdbg_clk"))
220                 init.ops = &dbgclk_ops;
221         else
222                 init.ops = &agilex_gateclk_ops;
223
224         init.name = clks->name;
225         init.flags = clks->flags;
226
227         init.num_parents = clks->num_parents;
228         init.parent_names = parent_name ? &parent_name : NULL;
229         if (init.parent_names == NULL)
230                 init.parent_data = clks->parent_data;
231         socfpga_clk->hw.hw.init = &init;
232
233         hw_clk = &socfpga_clk->hw.hw;
234
235         ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
236         if (ret) {
237                 kfree(socfpga_clk);
238                 return ERR_PTR(ret);
239         }
240         return hw_clk;
241 }