1 // SPDX-License-Identifier:	GPL-2.0
2 /*
3  * Copyright (C) 2017, Intel Corporation
4  */
5 #include <linux/slab.h>
6 #include <linux/clk-provider.h>
7 #include <linux/io.h>
8 
9 #include "stratix10-clk.h"
10 #include "clk.h"
11 
12 #define CLK_MGR_FREE_SHIFT		16
13 #define CLK_MGR_FREE_MASK		0x7
14 #define SWCTRLBTCLKSEN_SHIFT		8
15 
16 #define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
17 
18 static unsigned long n5x_clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
19 					     unsigned long parent_rate)
20 {
21 	struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
22 	unsigned long div;
23 	unsigned long shift = socfpgaclk->shift;
24 	u32 val;
25 
26 	val = readl(socfpgaclk->hw.reg);
27 	val &= (0x1f << shift);
28 	div = (val >> shift) + 1;
29 
30 	return parent_rate / div;
31 }
32 
33 static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
34 					     unsigned long parent_rate)
35 {
36 	struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
37 	unsigned long div = 1;
38 	u32 val;
39 
40 	val = readl(socfpgaclk->hw.reg);
41 	val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
42 	parent_rate /= val;
43 
44 	return parent_rate / div;
45 }
46 
47 static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
48 					     unsigned long parent_rate)
49 {
50 	struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
51 	unsigned long div = 1;
52 
53 	if (socfpgaclk->fixed_div) {
54 		div = socfpgaclk->fixed_div;
55 	} else {
56 		if (socfpgaclk->hw.reg)
57 			div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
58 	}
59 
60 	return parent_rate / div;
61 }
62 
63 static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
64 {
65 	struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
66 	u32 clk_src, mask;
67 	u8 parent;
68 
69 	if (socfpgaclk->bypass_reg) {
70 		mask = (0x1 << socfpgaclk->bypass_shift);
71 		parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
72 			   socfpgaclk->bypass_shift);
73 	} else {
74 		clk_src = readl(socfpgaclk->hw.reg);
75 		parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
76 			CLK_MGR_FREE_MASK;
77 	}
78 	return parent;
79 }
80 
81 static const struct clk_ops n5x_peri_c_clk_ops = {
82 	.recalc_rate = n5x_clk_peri_c_clk_recalc_rate,
83 	.get_parent = clk_periclk_get_parent,
84 };
85 
86 static const struct clk_ops peri_c_clk_ops = {
87 	.recalc_rate = clk_peri_c_clk_recalc_rate,
88 	.get_parent = clk_periclk_get_parent,
89 };
90 
91 static const struct clk_ops peri_cnt_clk_ops = {
92 	.recalc_rate = clk_peri_cnt_clk_recalc_rate,
93 	.get_parent = clk_periclk_get_parent,
94 };
95 
96 struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,
97 				void __iomem *reg)
98 {
99 	struct clk_hw *hw_clk;
100 	struct socfpga_periph_clk *periph_clk;
101 	struct clk_init_data init;
102 	const char *name = clks->name;
103 	const char *parent_name = clks->parent_name;
104 	int ret;
105 
106 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
107 	if (WARN_ON(!periph_clk))
108 		return NULL;
109 
110 	periph_clk->hw.reg = reg + clks->offset;
111 
112 	init.name = name;
113 	init.ops = &peri_c_clk_ops;
114 	init.flags = clks->flags;
115 
116 	init.num_parents = clks->num_parents;
117 	init.parent_names = parent_name ? &parent_name : NULL;
118 	if (init.parent_names == NULL)
119 		init.parent_data = clks->parent_data;
120 
121 	periph_clk->hw.hw.init = &init;
122 	hw_clk = &periph_clk->hw.hw;
123 
124 	ret = clk_hw_register(NULL, hw_clk);
125 	if (ret) {
126 		kfree(periph_clk);
127 		return ERR_PTR(ret);
128 	}
129 	return hw_clk;
130 }
131 
132 struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,
133 				void __iomem *regbase)
134 {
135 	struct clk_hw *hw_clk;
136 	struct socfpga_periph_clk *periph_clk;
137 	struct clk_init_data init;
138 	const char *name = clks->name;
139 	const char *parent_name = clks->parent_name;
140 	int ret;
141 
142 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
143 	if (WARN_ON(!periph_clk))
144 		return NULL;
145 
146 	periph_clk->hw.reg = regbase + clks->offset;
147 	periph_clk->shift = clks->shift;
148 
149 	init.name = name;
150 	init.ops = &n5x_peri_c_clk_ops;
151 	init.flags = clks->flags;
152 
153 	init.num_parents = clks->num_parents;
154 	init.parent_names = parent_name ? &parent_name : NULL;
155 
156 	periph_clk->hw.hw.init = &init;
157 	hw_clk = &periph_clk->hw.hw;
158 
159 	ret = clk_hw_register(NULL, hw_clk);
160 	if (ret) {
161 		kfree(periph_clk);
162 		return ERR_PTR(ret);
163 	}
164 	return hw_clk;
165 }
166 
167 struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
168 				    void __iomem *regbase)
169 {
170 	struct clk_hw *hw_clk;
171 	struct socfpga_periph_clk *periph_clk;
172 	struct clk_init_data init;
173 	const char *name = clks->name;
174 	const char *parent_name = clks->parent_name;
175 	int ret;
176 
177 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
178 	if (WARN_ON(!periph_clk))
179 		return NULL;
180 
181 	if (clks->offset)
182 		periph_clk->hw.reg = regbase + clks->offset;
183 	else
184 		periph_clk->hw.reg = NULL;
185 
186 	if (clks->bypass_reg)
187 		periph_clk->bypass_reg = regbase + clks->bypass_reg;
188 	else
189 		periph_clk->bypass_reg = NULL;
190 	periph_clk->bypass_shift = clks->bypass_shift;
191 	periph_clk->fixed_div = clks->fixed_divider;
192 
193 	init.name = name;
194 	init.ops = &peri_cnt_clk_ops;
195 	init.flags = clks->flags;
196 
197 	init.num_parents = clks->num_parents;
198 	init.parent_names = parent_name ? &parent_name : NULL;
199 	if (init.parent_names == NULL)
200 		init.parent_data = clks->parent_data;
201 
202 	periph_clk->hw.hw.init = &init;
203 	hw_clk = &periph_clk->hw.hw;
204 
205 	ret = clk_hw_register(NULL, hw_clk);
206 	if (ret) {
207 		kfree(periph_clk);
208 		return ERR_PTR(ret);
209 	}
210 	return hw_clk;
211 }
212