xref: /linux/drivers/clk/socfpga/clk-gate-s10.c (revision 0be3ff0c)
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 }
242