File Index
Files and directories
Sign in
FILE

cpg.c

File preview with syntax highlighting.

linux-6.19.14/drivers/sh/clk/cpg.c C
  1/*
  2 * Helper routines for SuperH Clock Pulse Generator blocks (CPG).
  3 *
  4 *  Copyright (C) 2010  Magnus Damm
  5 *  Copyright (C) 2010 - 2012  Paul Mundt
  6 *
  7 * This file is subject to the terms and conditions of the GNU General Public
  8 * License.  See the file "COPYING" in the main directory of this archive
  9 * for more details.
 10 */
 11#include <linux/clk.h>
 12#include <linux/compiler.h>
 13#include <linux/slab.h>
 14#include <linux/io.h>
 15#include <linux/sh_clk.h>
 16
 17#define CPG_CKSTP_BIT	BIT(8)
 18
 19static unsigned int sh_clk_read(struct clk *clk)
 20{
 21	if (clk->flags & CLK_ENABLE_REG_8BIT)
 22		return ioread8(clk->mapped_reg);
 23	else if (clk->flags & CLK_ENABLE_REG_16BIT)
 24		return ioread16(clk->mapped_reg);
 25
 26	return ioread32(clk->mapped_reg);
 27}
 28
 29static unsigned int sh_clk_read_status(struct clk *clk)
 30{
 31	void __iomem *mapped_status = (phys_addr_t)clk->status_reg -
 32		(phys_addr_t)clk->enable_reg + clk->mapped_reg;
 33
 34	if (clk->flags & CLK_ENABLE_REG_8BIT)
 35		return ioread8(mapped_status);
 36	else if (clk->flags & CLK_ENABLE_REG_16BIT)
 37		return ioread16(mapped_status);
 38
 39	return ioread32(mapped_status);
 40}
 41
 42static void sh_clk_write(int value, struct clk *clk)
 43{
 44	if (clk->flags & CLK_ENABLE_REG_8BIT)
 45		iowrite8(value, clk->mapped_reg);
 46	else if (clk->flags & CLK_ENABLE_REG_16BIT)
 47		iowrite16(value, clk->mapped_reg);
 48	else
 49		iowrite32(value, clk->mapped_reg);
 50}
 51
 52static int sh_clk_mstp_enable(struct clk *clk)
 53{
 54	sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk);
 55	if (clk->status_reg) {
 56		int i;
 57
 58		for (i = 1000;
 59		     (sh_clk_read_status(clk) & (1 << clk->enable_bit)) && i;
 60		     i--)
 61			cpu_relax();
 62		if (!i) {
 63			pr_err("cpg: failed to enable %p[%d]\n",
 64			       clk->enable_reg, clk->enable_bit);
 65			return -ETIMEDOUT;
 66		}
 67	}
 68	return 0;
 69}
 70
 71static void sh_clk_mstp_disable(struct clk *clk)
 72{
 73	sh_clk_write(sh_clk_read(clk) | (1 << clk->enable_bit), clk);
 74}
 75
 76static struct sh_clk_ops sh_clk_mstp_clk_ops = {
 77	.enable		= sh_clk_mstp_enable,
 78	.disable	= sh_clk_mstp_disable,
 79	.recalc		= followparent_recalc,
 80};
 81
 82int __init sh_clk_mstp_register(struct clk *clks, int nr)
 83{
 84	struct clk *clkp;
 85	int ret = 0;
 86	int k;
 87
 88	for (k = 0; !ret && (k < nr); k++) {
 89		clkp = clks + k;
 90		clkp->ops = &sh_clk_mstp_clk_ops;
 91		ret |= clk_register(clkp);
 92	}
 93
 94	return ret;
 95}
 96
 97/*
 98 * Div/mult table lookup helpers
 99 */
100static inline struct clk_div_table *clk_to_div_table(struct clk *clk)
101{
102	return clk->priv;
103}
104
105static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk)
106{
107	return clk_to_div_table(clk)->div_mult_table;
108}
109
110/*
111 * Common div ops
112 */
113static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
114{
115	return clk_rate_table_round(clk, clk->freq_table, rate);
116}
117
118static unsigned long sh_clk_div_recalc(struct clk *clk)
119{
120	struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
121	unsigned int idx;
122
123	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
124			     table, clk->arch_flags ? &clk->arch_flags : NULL);
125
126	idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask;
127
128	return clk->freq_table[idx].frequency;
129}
130
131static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
132{
133	struct clk_div_table *dt = clk_to_div_table(clk);
134	unsigned long value;
135	int idx;
136
137	idx = clk_rate_table_find(clk, clk->freq_table, rate);
138	if (idx < 0)
139		return idx;
140
141	value = sh_clk_read(clk);
142	value &= ~(clk->div_mask << clk->enable_bit);
143	value |= (idx << clk->enable_bit);
144	sh_clk_write(value, clk);
145
146	/* XXX: Should use a post-change notifier */
147	if (dt->kick)
148		dt->kick(clk);
149
150	return 0;
151}
152
153static int sh_clk_div_enable(struct clk *clk)
154{
155	if (clk->div_mask == SH_CLK_DIV6_MSK) {
156		int ret = sh_clk_div_set_rate(clk, clk->rate);
157		if (ret < 0)
158			return ret;
159	}
160
161	sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
162	return 0;
163}
164
165static void sh_clk_div_disable(struct clk *clk)
166{
167	unsigned int val;
168
169	val = sh_clk_read(clk);
170	val |= CPG_CKSTP_BIT;
171
172	/*
173	 * div6 clocks require the divisor field to be non-zero or the
174	 * above CKSTP toggle silently fails. Ensure that the divisor
175	 * array is reset to its initial state on disable.
176	 */
177	if (clk->flags & CLK_MASK_DIV_ON_DISABLE)
178		val |= clk->div_mask;
179
180	sh_clk_write(val, clk);
181}
182
183static struct sh_clk_ops sh_clk_div_clk_ops = {
184	.recalc		= sh_clk_div_recalc,
185	.set_rate	= sh_clk_div_set_rate,
186	.round_rate	= sh_clk_div_round_rate,
187};
188
189static struct sh_clk_ops sh_clk_div_enable_clk_ops = {
190	.recalc		= sh_clk_div_recalc,
191	.set_rate	= sh_clk_div_set_rate,
192	.round_rate	= sh_clk_div_round_rate,
193	.enable		= sh_clk_div_enable,
194	.disable	= sh_clk_div_disable,
195};
196
197static int __init sh_clk_init_parent(struct clk *clk)
198{
199	u32 val;
200
201	if (clk->parent)
202		return 0;
203
204	if (!clk->parent_table || !clk->parent_num)
205		return 0;
206
207	if (!clk->src_width) {
208		pr_err("sh_clk_init_parent: cannot select parent clock\n");
209		return -EINVAL;
210	}
211
212	val  = (sh_clk_read(clk) >> clk->src_shift);
213	val &= (1 << clk->src_width) - 1;
214
215	if (val >= clk->parent_num) {
216		pr_err("sh_clk_init_parent: parent table size failed\n");
217		return -EINVAL;
218	}
219
220	clk_reparent(clk, clk->parent_table[val]);
221	if (!clk->parent) {
222		pr_err("sh_clk_init_parent: unable to set parent");
223		return -EINVAL;
224	}
225
226	return 0;
227}
228
229static int __init sh_clk_div_register_ops(struct clk *clks, int nr,
230			struct clk_div_table *table, struct sh_clk_ops *ops)
231{
232	struct clk *clkp;
233	void *freq_table;
234	int nr_divs = table->div_mult_table->nr_divisors;
235	int freq_table_size = sizeof(struct cpufreq_frequency_table);
236	int ret = 0;
237	int k;
238
239	freq_table_size *= (nr_divs + 1);
240	freq_table = kcalloc(nr, freq_table_size, GFP_KERNEL);
241	if (!freq_table) {
242		pr_err("%s: unable to alloc memory\n", __func__);
243		return -ENOMEM;
244	}
245
246	for (k = 0; !ret && (k < nr); k++) {
247		clkp = clks + k;
248
249		clkp->ops = ops;
250		clkp->priv = table;
251
252		clkp->freq_table = freq_table + (k * freq_table_size);
253		clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
254
255		ret = clk_register(clkp);
256		if (ret == 0)
257			ret = sh_clk_init_parent(clkp);
258	}
259
260	return ret;
261}
262
263/*
264 * div6 support
265 */
266static int sh_clk_div6_divisors[64] = {
267	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
268	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
269	33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
270	49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
271};
272
273static struct clk_div_mult_table div6_div_mult_table = {
274	.divisors = sh_clk_div6_divisors,
275	.nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
276};
277
278static struct clk_div_table sh_clk_div6_table = {
279	.div_mult_table	= &div6_div_mult_table,
280};
281
282static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
283{
284	struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
285	u32 value;
286	int ret, i;
287
288	if (!clk->parent_table || !clk->parent_num)
289		return -EINVAL;
290
291	/* Search the parent */
292	for (i = 0; i < clk->parent_num; i++)
293		if (clk->parent_table[i] == parent)
294			break;
295
296	if (i == clk->parent_num)
297		return -ENODEV;
298
299	ret = clk_reparent(clk, parent);
300	if (ret < 0)
301		return ret;
302
303	value = sh_clk_read(clk) &
304		~(((1 << clk->src_width) - 1) << clk->src_shift);
305
306	sh_clk_write(value | (i << clk->src_shift), clk);
307
308	/* Rebuild the frequency table */
309	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
310			     table, NULL);
311
312	return 0;
313}
314
315static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
316	.recalc		= sh_clk_div_recalc,
317	.round_rate	= sh_clk_div_round_rate,
318	.set_rate	= sh_clk_div_set_rate,
319	.enable		= sh_clk_div_enable,
320	.disable	= sh_clk_div_disable,
321	.set_parent	= sh_clk_div6_set_parent,
322};
323
324int __init sh_clk_div6_register(struct clk *clks, int nr)
325{
326	return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
327				       &sh_clk_div_enable_clk_ops);
328}
329
330int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
331{
332	return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
333				       &sh_clk_div6_reparent_clk_ops);
334}
335
336/*
337 * div4 support
338 */
339static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
340{
341	struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
342	u32 value;
343	int ret;
344
345	/* we really need a better way to determine parent index, but for
346	 * now assume internal parent comes with CLK_ENABLE_ON_INIT set,
347	 * no CLK_ENABLE_ON_INIT means external clock...
348	 */
349
350	if (parent->flags & CLK_ENABLE_ON_INIT)
351		value = sh_clk_read(clk) & ~(1 << 7);
352	else
353		value = sh_clk_read(clk) | (1 << 7);
354
355	ret = clk_reparent(clk, parent);
356	if (ret < 0)
357		return ret;
358
359	sh_clk_write(value, clk);
360
361	/* Rebiuld the frequency table */
362	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
363			     table, &clk->arch_flags);
364
365	return 0;
366}
367
368static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
369	.recalc		= sh_clk_div_recalc,
370	.set_rate	= sh_clk_div_set_rate,
371	.round_rate	= sh_clk_div_round_rate,
372	.enable		= sh_clk_div_enable,
373	.disable	= sh_clk_div_disable,
374	.set_parent	= sh_clk_div4_set_parent,
375};
376
377int __init sh_clk_div4_register(struct clk *clks, int nr,
378				struct clk_div4_table *table)
379{
380	return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops);
381}
382
383int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
384				struct clk_div4_table *table)
385{
386	return sh_clk_div_register_ops(clks, nr, table,
387				       &sh_clk_div_enable_clk_ops);
388}
389
390int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
391				struct clk_div4_table *table)
392{
393	return sh_clk_div_register_ops(clks, nr, table,
394				       &sh_clk_div4_reparent_clk_ops);
395}
396
397/* FSI-DIV */
398static unsigned long fsidiv_recalc(struct clk *clk)
399{
400	u32 value;
401
402	value = __raw_readl(clk->mapping->base);
403
404	value >>= 16;
405	if (value < 2)
406		return clk->parent->rate;
407
408	return clk->parent->rate / value;
409}
410
411static long fsidiv_round_rate(struct clk *clk, unsigned long rate)
412{
413	return clk_rate_div_range_round(clk, 1, 0xffff, rate);
414}
415
416static void fsidiv_disable(struct clk *clk)
417{
418	__raw_writel(0, clk->mapping->base);
419}
420
421static int fsidiv_enable(struct clk *clk)
422{
423	u32 value;
424
425	value  = __raw_readl(clk->mapping->base) >> 16;
426	if (value < 2)
427		return 0;
428
429	__raw_writel((value << 16) | 0x3, clk->mapping->base);
430
431	return 0;
432}
433
434static int fsidiv_set_rate(struct clk *clk, unsigned long rate)
435{
436	int idx;
437
438	idx = (clk->parent->rate / rate) & 0xffff;
439	if (idx < 2)
440		__raw_writel(0, clk->mapping->base);
441	else
442		__raw_writel(idx << 16, clk->mapping->base);
443
444	return 0;
445}
446
447static struct sh_clk_ops fsidiv_clk_ops = {
448	.recalc		= fsidiv_recalc,
449	.round_rate	= fsidiv_round_rate,
450	.set_rate	= fsidiv_set_rate,
451	.enable		= fsidiv_enable,
452	.disable	= fsidiv_disable,
453};
454
455int __init sh_clk_fsidiv_register(struct clk *clks, int nr)
456{
457	struct clk_mapping *map;
458	int i;
459
460	for (i = 0; i < nr; i++) {
461
462		map = kzalloc(sizeof(struct clk_mapping), GFP_KERNEL);
463		if (!map) {
464			pr_err("%s: unable to alloc memory\n", __func__);
465			return -ENOMEM;
466		}
467
468		/* clks[i].enable_reg came from SH_CLK_FSIDIV() */
469		map->phys		= (phys_addr_t)clks[i].enable_reg;
470		map->len		= 8;
471
472		clks[i].enable_reg	= 0; /* remove .enable_reg */
473		clks[i].ops		= &fsidiv_clk_ops;
474		clks[i].mapping		= map;
475
476		clk_register(&clks[i]);
477	}
478
479	return 0;
480}
~/ Read-only file preview.