1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2017, Intel Corporation
5 #include <linux/clk-provider.h>
7 #include <linux/slab.h>
8 #include "stratix10-clk.h"
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)
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
21 static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw
*hwclk
,
22 unsigned long parent_rate
)
24 struct socfpga_gate_clk
*socfpgaclk
= to_socfpga_gate_clk(hwclk
);
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);
34 return parent_rate
/ div
;
37 static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw
*hwclk
,
38 unsigned long parent_rate
)
40 struct socfpga_gate_clk
*socfpgaclk
= to_socfpga_gate_clk(hwclk
);
43 val
= readl(socfpgaclk
->div_reg
) >> socfpgaclk
->shift
;
44 val
&= GENMASK(socfpgaclk
->width
- 1, 0);
48 return parent_rate
/ div
;
51 static u8
socfpga_gate_get_parent(struct clk_hw
*hwclk
)
53 struct socfpga_gate_clk
*socfpgaclk
= to_socfpga_gate_clk(hwclk
);
54 u32 mask
, second_bypass
;
56 const char *name
= clk_hw_get_name(hwclk
);
58 if (socfpgaclk
->bypass_reg
) {
59 mask
= (0x1 << socfpgaclk
->bypass_shift
);
60 parent
= ((readl(socfpgaclk
->bypass_reg
) & mask
) >>
61 socfpgaclk
->bypass_shift
);
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
;
74 if (second_bypass
& 0x2)
75 if (parent
== 1) /* only applicable if parent is macb */
76 parent
= BOOTCLK_BYPASS
;
81 static u8
socfpga_agilex_gate_get_parent(struct clk_hw
*hwclk
)
83 struct socfpga_gate_clk
*socfpgaclk
= to_socfpga_gate_clk(hwclk
);
84 u32 mask
, second_bypass
;
86 const char *name
= clk_hw_get_name(hwclk
);
88 if (socfpgaclk
->bypass_reg
) {
89 mask
= (0x1 << socfpgaclk
->bypass_shift
);
90 parent
= ((readl(socfpgaclk
->bypass_reg
) & mask
) >>
91 socfpgaclk
->bypass_shift
);
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
;
104 if (second_bypass
& 0x2)
105 if (parent
== 1) /* only applicable if parent is macb */
106 parent
= BOOTCLK_BYPASS
;
112 static struct clk_ops gateclk_ops
= {
113 .recalc_rate
= socfpga_gate_clk_recalc_rate
,
114 .get_parent
= socfpga_gate_get_parent
,
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
,
122 static const struct clk_ops dbgclk_ops
= {
123 .recalc_rate
= socfpga_dbg_clk_recalc_rate
,
124 .get_parent
= socfpga_gate_get_parent
,
127 struct clk_hw
*s10_register_gate(const struct stratix10_gate_clock
*clks
, void __iomem
*regbase
)
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
;
135 socfpga_clk
= kzalloc(sizeof(*socfpga_clk
), GFP_KERNEL
);
139 socfpga_clk
->hw
.reg
= regbase
+ clks
->gate_reg
;
140 socfpga_clk
->hw
.bit_idx
= clks
->gate_idx
;
142 gateclk_ops
.enable
= clk_gate_ops
.enable
;
143 gateclk_ops
.disable
= clk_gate_ops
.disable
;
145 socfpga_clk
->fixed_div
= clks
->fixed_div
;
148 socfpga_clk
->div_reg
= regbase
+ clks
->div_reg
;
150 socfpga_clk
->div_reg
= NULL
;
152 socfpga_clk
->width
= clks
->div_width
;
153 socfpga_clk
->shift
= clks
->div_offset
;
155 if (clks
->bypass_reg
)
156 socfpga_clk
->bypass_reg
= regbase
+ clks
->bypass_reg
;
158 socfpga_clk
->bypass_reg
= NULL
;
159 socfpga_clk
->bypass_shift
= clks
->bypass_shift
;
161 if (streq(clks
->name
, "cs_pdbg_clk"))
162 init
.ops
= &dbgclk_ops
;
164 init
.ops
= &gateclk_ops
;
166 init
.name
= clks
->name
;
167 init
.flags
= clks
->flags
;
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
;
175 hw_clk
= &socfpga_clk
->hw
.hw
;
177 ret
= clk_hw_register(NULL
, &socfpga_clk
->hw
.hw
);
185 struct clk_hw
*agilex_register_gate(const struct stratix10_gate_clock
*clks
, void __iomem
*regbase
)
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
;
193 socfpga_clk
= kzalloc(sizeof(*socfpga_clk
), GFP_KERNEL
);
197 socfpga_clk
->hw
.reg
= regbase
+ clks
->gate_reg
;
198 socfpga_clk
->hw
.bit_idx
= clks
->gate_idx
;
200 gateclk_ops
.enable
= clk_gate_ops
.enable
;
201 gateclk_ops
.disable
= clk_gate_ops
.disable
;
203 socfpga_clk
->fixed_div
= clks
->fixed_div
;
206 socfpga_clk
->div_reg
= regbase
+ clks
->div_reg
;
208 socfpga_clk
->div_reg
= NULL
;
210 socfpga_clk
->width
= clks
->div_width
;
211 socfpga_clk
->shift
= clks
->div_offset
;
213 if (clks
->bypass_reg
)
214 socfpga_clk
->bypass_reg
= regbase
+ clks
->bypass_reg
;
216 socfpga_clk
->bypass_reg
= NULL
;
217 socfpga_clk
->bypass_shift
= clks
->bypass_shift
;
219 if (streq(clks
->name
, "cs_pdbg_clk"))
220 init
.ops
= &dbgclk_ops
;
222 init
.ops
= &agilex_gateclk_ops
;
224 init
.name
= clks
->name
;
225 init
.flags
= clks
->flags
;
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
;
233 hw_clk
= &socfpga_clk
->hw
.hw
;
235 ret
= clk_hw_register(NULL
, &socfpga_clk
->hw
.hw
);