File Index
Files and directories
Sign in
FILE

clk-periph-s10.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/socfpga/clk-periph-s10.c C
  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
 18static 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
 33static 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
 47static 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
 63static 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 = 0;
 68
 69	/* handle the bypass first */
 70	if (socfpgaclk->bypass_reg) {
 71		mask = (0x1 << socfpgaclk->bypass_shift);
 72		parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
 73			   socfpgaclk->bypass_shift);
 74		if (parent)
 75			return parent;
 76	}
 77
 78	if (socfpgaclk->hw.reg) {
 79		clk_src = readl(socfpgaclk->hw.reg);
 80		parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
 81			  CLK_MGR_FREE_MASK;
 82	}
 83	return parent;
 84}
 85
 86static const struct clk_ops n5x_peri_c_clk_ops = {
 87	.recalc_rate = n5x_clk_peri_c_clk_recalc_rate,
 88	.get_parent = clk_periclk_get_parent,
 89};
 90
 91static const struct clk_ops peri_c_clk_ops = {
 92	.recalc_rate = clk_peri_c_clk_recalc_rate,
 93	.get_parent = clk_periclk_get_parent,
 94};
 95
 96static const struct clk_ops peri_cnt_clk_ops = {
 97	.recalc_rate = clk_peri_cnt_clk_recalc_rate,
 98	.get_parent = clk_periclk_get_parent,
 99};
100
101struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,
102				void __iomem *reg)
103{
104	struct clk_hw *hw_clk;
105	struct socfpga_periph_clk *periph_clk;
106	struct clk_init_data init;
107	const char *name = clks->name;
108	const char *parent_name = clks->parent_name;
109	int ret;
110
111	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
112	if (WARN_ON(!periph_clk))
113		return NULL;
114
115	periph_clk->hw.reg = reg + clks->offset;
116
117	init.name = name;
118	init.ops = &peri_c_clk_ops;
119	init.flags = clks->flags;
120
121	init.num_parents = clks->num_parents;
122	init.parent_names = parent_name ? &parent_name : NULL;
123	if (init.parent_names == NULL)
124		init.parent_data = clks->parent_data;
125
126	periph_clk->hw.hw.init = &init;
127	hw_clk = &periph_clk->hw.hw;
128
129	ret = clk_hw_register(NULL, hw_clk);
130	if (ret) {
131		kfree(periph_clk);
132		return ERR_PTR(ret);
133	}
134	return hw_clk;
135}
136
137struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,
138				void __iomem *regbase)
139{
140	struct clk_hw *hw_clk;
141	struct socfpga_periph_clk *periph_clk;
142	struct clk_init_data init;
143	const char *name = clks->name;
144	const char *parent_name = clks->parent_name;
145	int ret;
146
147	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
148	if (WARN_ON(!periph_clk))
149		return NULL;
150
151	periph_clk->hw.reg = regbase + clks->offset;
152	periph_clk->shift = clks->shift;
153
154	init.name = name;
155	init.ops = &n5x_peri_c_clk_ops;
156	init.flags = clks->flags;
157
158	init.num_parents = clks->num_parents;
159	init.parent_names = parent_name ? &parent_name : NULL;
160
161	periph_clk->hw.hw.init = &init;
162	hw_clk = &periph_clk->hw.hw;
163
164	ret = clk_hw_register(NULL, hw_clk);
165	if (ret) {
166		kfree(periph_clk);
167		return ERR_PTR(ret);
168	}
169	return hw_clk;
170}
171
172struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
173				    void __iomem *regbase)
174{
175	struct clk_hw *hw_clk;
176	struct socfpga_periph_clk *periph_clk;
177	struct clk_init_data init;
178	const char *name = clks->name;
179	const char *parent_name = clks->parent_name;
180	int ret;
181
182	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
183	if (WARN_ON(!periph_clk))
184		return NULL;
185
186	if (clks->offset)
187		periph_clk->hw.reg = regbase + clks->offset;
188	else
189		periph_clk->hw.reg = NULL;
190
191	if (clks->bypass_reg)
192		periph_clk->bypass_reg = regbase + clks->bypass_reg;
193	else
194		periph_clk->bypass_reg = NULL;
195	periph_clk->bypass_shift = clks->bypass_shift;
196	periph_clk->fixed_div = clks->fixed_divider;
197
198	init.name = name;
199	init.ops = &peri_cnt_clk_ops;
200	init.flags = clks->flags;
201
202	init.num_parents = clks->num_parents;
203	init.parent_names = parent_name ? &parent_name : NULL;
204	if (init.parent_names == NULL)
205		init.parent_data = clks->parent_data;
206
207	periph_clk->hw.hw.init = &init;
208	hw_clk = &periph_clk->hw.hw;
209
210	ret = clk_hw_register(NULL, hw_clk);
211	if (ret) {
212		kfree(periph_clk);
213		return ERR_PTR(ret);
214	}
215	return hw_clk;
216}
217
218struct clk_hw *agilex5_register_cnt_periph(const struct agilex5_perip_cnt_clock *clks,
219					   void __iomem *regbase)
220{
221	struct clk_hw *hw_clk;
222	struct socfpga_periph_clk *periph_clk;
223	struct clk_init_data init;
224	const char *name = clks->name;
225	int ret;
226
227	periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
228	if (WARN_ON(!periph_clk))
229		return NULL;
230
231	if (clks->offset)
232		periph_clk->hw.reg = regbase + clks->offset;
233	else
234		periph_clk->hw.reg = NULL;
235
236	if (clks->bypass_reg)
237		periph_clk->bypass_reg = regbase + clks->bypass_reg;
238	else
239		periph_clk->bypass_reg = NULL;
240	periph_clk->bypass_shift = clks->bypass_shift;
241	periph_clk->fixed_div = clks->fixed_divider;
242
243	init.name = name;
244	init.ops = &peri_cnt_clk_ops;
245	init.flags = clks->flags;
246	init.num_parents = clks->num_parents;
247	init.parent_names = clks->parent_names;
248	periph_clk->hw.hw.init = &init;
249	hw_clk = &periph_clk->hw.hw;
250
251	ret = clk_hw_register(NULL, hw_clk);
252	if (ret) {
253		kfree(periph_clk);
254		return ERR_PTR(ret);
255	}
256	return hw_clk;
257}
~/ Read-only file preview.