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(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(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(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
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(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
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(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
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(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