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 *s10_register_periph(const struct stratix10_perip_c_clock *clks,
97 				void __iomem *reg)
98 {
99 	struct clk *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 
105 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
106 	if (WARN_ON(!periph_clk))
107 		return NULL;
108 
109 	periph_clk->hw.reg = reg + clks->offset;
110 
111 	init.name = name;
112 	init.ops = &peri_c_clk_ops;
113 	init.flags = clks->flags;
114 
115 	init.num_parents = clks->num_parents;
116 	init.parent_names = parent_name ? &parent_name : NULL;
117 	if (init.parent_names == NULL)
118 		init.parent_data = clks->parent_data;
119 
120 	periph_clk->hw.hw.init = &init;
121 
122 	clk = clk_register(NULL, &periph_clk->hw.hw);
123 	if (WARN_ON(IS_ERR(clk))) {
124 		kfree(periph_clk);
125 		return NULL;
126 	}
127 	return clk;
128 }
129 
130 struct clk *n5x_register_periph(const struct n5x_perip_c_clock *clks,
131 				void __iomem *regbase)
132 {
133 	struct clk *clk;
134 	struct socfpga_periph_clk *periph_clk;
135 	struct clk_init_data init;
136 	const char *name = clks->name;
137 	const char *parent_name = clks->parent_name;
138 
139 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
140 	if (WARN_ON(!periph_clk))
141 		return NULL;
142 
143 	periph_clk->hw.reg = regbase + clks->offset;
144 	periph_clk->shift = clks->shift;
145 
146 	init.name = name;
147 	init.ops = &n5x_peri_c_clk_ops;
148 	init.flags = clks->flags;
149 
150 	init.num_parents = clks->num_parents;
151 	init.parent_names = parent_name ? &parent_name : NULL;
152 
153 	periph_clk->hw.hw.init = &init;
154 
155 	clk = clk_register(NULL, &periph_clk->hw.hw);
156 	if (WARN_ON(IS_ERR(clk))) {
157 		kfree(periph_clk);
158 		return NULL;
159 	}
160 	return clk;
161 }
162 
163 struct clk *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
164 				    void __iomem *regbase)
165 {
166 	struct clk *clk;
167 	struct socfpga_periph_clk *periph_clk;
168 	struct clk_init_data init;
169 	const char *name = clks->name;
170 	const char *parent_name = clks->parent_name;
171 
172 	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
173 	if (WARN_ON(!periph_clk))
174 		return NULL;
175 
176 	if (clks->offset)
177 		periph_clk->hw.reg = regbase + clks->offset;
178 	else
179 		periph_clk->hw.reg = NULL;
180 
181 	if (clks->bypass_reg)
182 		periph_clk->bypass_reg = regbase + clks->bypass_reg;
183 	else
184 		periph_clk->bypass_reg = NULL;
185 	periph_clk->bypass_shift = clks->bypass_shift;
186 	periph_clk->fixed_div = clks->fixed_divider;
187 
188 	init.name = name;
189 	init.ops = &peri_cnt_clk_ops;
190 	init.flags = clks->flags;
191 
192 	init.num_parents = clks->num_parents;
193 	init.parent_names = parent_name ? &parent_name : NULL;
194 	if (init.parent_names == NULL)
195 		init.parent_data = clks->parent_data;
196 
197 	periph_clk->hw.hw.init = &init;
198 
199 	clk = clk_register(NULL, &periph_clk->hw.hw);
200 	if (WARN_ON(IS_ERR(clk))) {
201 		kfree(periph_clk);
202 		return NULL;
203 	}
204 	return clk;
205 }
206