blob: 32567795765fb5bb6b03a05e50614f065171ded1 [file] [log] [blame]
Dinh Nguyen07afb8d2018-03-21 09:20:12 -05001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2017, Intel Corporation
4 */
5#include <linux/clk-provider.h>
Stephen Boyd62e59c42019-04-18 15:20:22 -07006#include <linux/io.h>
Dinh Nguyen07afb8d2018-03-21 09:20:12 -05007#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
Dinh Nguyenc2c9c562021-06-10 21:52:00 -050014#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
Dinh Nguyen07afb8d2018-03-21 09:20:12 -050021static 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);
Colin Ian King52d1a8d2021-04-06 19:27:46 +010041 u32 div, val;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -050042
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);
Dinh Nguyenc2c9c562021-06-10 21:52:00 -050054 u32 mask, second_bypass;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -050055 u8 parent = 0;
Dinh Nguyenc2c9c562021-06-10 21:52:00 -050056 const char *name = clk_hw_get_name(hwclk);
Dinh Nguyen07afb8d2018-03-21 09:20:12 -050057
58 if (socfpgaclk->bypass_reg) {
59 mask = (0x1 << socfpgaclk->bypass_shift);
60 parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
61 socfpgaclk->bypass_shift);
62 }
Dinh Nguyenc2c9c562021-06-10 21:52:00 -050063
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
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500109 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
Dinh Nguyenc2c9c562021-06-10 21:52:00 -0500117static 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
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500122static const struct clk_ops dbgclk_ops = {
123 .recalc_rate = socfpga_dbg_clk_recalc_rate,
124 .get_parent = socfpga_gate_get_parent,
125};
126
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600127struct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500128{
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600129 struct clk_hw *hw_clk;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500130 struct socfpga_gate_clk *socfpga_clk;
131 struct clk_init_data init;
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600132 const char *parent_name = clks->parent_name;
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600133 int ret;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500134
135 socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
136 if (!socfpga_clk)
137 return NULL;
138
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600139 socfpga_clk->hw.reg = regbase + clks->gate_reg;
140 socfpga_clk->hw.bit_idx = clks->gate_idx;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500141
142 gateclk_ops.enable = clk_gate_ops.enable;
143 gateclk_ops.disable = clk_gate_ops.disable;
144
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600145 socfpga_clk->fixed_div = clks->fixed_div;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500146
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600147 if (clks->div_reg)
148 socfpga_clk->div_reg = regbase + clks->div_reg;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500149 else
150 socfpga_clk->div_reg = NULL;
151
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600152 socfpga_clk->width = clks->div_width;
153 socfpga_clk->shift = clks->div_offset;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500154
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600155 if (clks->bypass_reg)
156 socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500157 else
158 socfpga_clk->bypass_reg = NULL;
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600159 socfpga_clk->bypass_shift = clks->bypass_shift;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500160
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600161 if (streq(clks->name, "cs_pdbg_clk"))
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500162 init.ops = &dbgclk_ops;
163 else
164 init.ops = &gateclk_ops;
165
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600166 init.name = clks->name;
167 init.flags = clks->flags;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500168
Dinh Nguyen8c0e7832020-01-14 10:07:26 -0600169 init.num_parents = clks->num_parents;
Dinh Nguyen762d9612020-05-12 13:16:43 -0500170 init.parent_names = parent_name ? &parent_name : NULL;
171 if (init.parent_names == NULL)
172 init.parent_data = clks->parent_data;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500173 socfpga_clk->hw.hw.init = &init;
174
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600175 hw_clk = &socfpga_clk->hw.hw;
176
177 ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
178 if (ret) {
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500179 kfree(socfpga_clk);
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600180 return ERR_PTR(ret);
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500181 }
Dinh Nguyenba7e2582021-03-02 15:41:51 -0600182 return hw_clk;
Dinh Nguyen07afb8d2018-03-21 09:20:12 -0500183}
Dinh Nguyenc2c9c562021-06-10 21:52:00 -0500184
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}