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
21static 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(addr: socfpgaclk->div_reg) >> socfpgaclk->shift;
31 val &= GENMASK(socfpgaclk->width - 1, 0);
32 div = (1 << val);
33 }
34 return parent_rate / div;
35}
36
37static 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(addr: 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
51static 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(hw: hwclk);
57
58 if (socfpgaclk->bypass_reg) {
59 mask = (0x1 << socfpgaclk->bypass_shift);
60 parent = ((readl(addr: 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(addr: 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
81static 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(hw: hwclk);
87
88 if (socfpgaclk->bypass_reg) {
89 mask = (0x1 << socfpgaclk->bypass_shift);
90 parent = ((readl(addr: 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(addr: 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
112static struct clk_ops gateclk_ops = {
113 .recalc_rate = socfpga_gate_clk_recalc_rate,
114 .get_parent = socfpga_gate_get_parent,
115};
116
117static 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
122static const struct clk_ops dbgclk_ops = {
123 .recalc_rate = socfpga_dbg_clk_recalc_rate,
124 .get_parent = socfpga_gate_get_parent,
125};
126
127struct 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(size: 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, hw: &socfpga_clk->hw.hw);
178 if (ret) {
179 kfree(objp: socfpga_clk);
180 return ERR_PTR(error: ret);
181 }
182 return hw_clk;
183}
184
185struct 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(size: 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, hw: &socfpga_clk->hw.hw);
236 if (ret) {
237 kfree(objp: socfpga_clk);
238 return ERR_PTR(error: ret);
239 }
240 return hw_clk;
241}
242

source code of linux/drivers/clk/socfpga/clk-gate-s10.c