xref: /openbmc/linux/drivers/clk/sunxi/clk-mod0.c (revision 992a56e48996d4dea6cc25a35e180f696935925d)
1*992a56e4SMaxime Ripard /*
2*992a56e4SMaxime Ripard  * Copyright 2013 Emilio López
3*992a56e4SMaxime Ripard  *
4*992a56e4SMaxime Ripard  * Emilio López <emilio@elopez.com.ar>
5*992a56e4SMaxime Ripard  *
6*992a56e4SMaxime Ripard  * This program is free software; you can redistribute it and/or modify
7*992a56e4SMaxime Ripard  * it under the terms of the GNU General Public License as published by
8*992a56e4SMaxime Ripard  * the Free Software Foundation; either version 2 of the License, or
9*992a56e4SMaxime Ripard  * (at your option) any later version.
10*992a56e4SMaxime Ripard  *
11*992a56e4SMaxime Ripard  * This program is distributed in the hope that it will be useful,
12*992a56e4SMaxime Ripard  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*992a56e4SMaxime Ripard  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*992a56e4SMaxime Ripard  * GNU General Public License for more details.
15*992a56e4SMaxime Ripard  */
16*992a56e4SMaxime Ripard 
17*992a56e4SMaxime Ripard #include <linux/clk-provider.h>
18*992a56e4SMaxime Ripard #include <linux/clkdev.h>
19*992a56e4SMaxime Ripard 
20*992a56e4SMaxime Ripard #include "clk-factors.h"
21*992a56e4SMaxime Ripard 
22*992a56e4SMaxime Ripard /**
23*992a56e4SMaxime Ripard  * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
24*992a56e4SMaxime Ripard  * MOD0 rate is calculated as follows
25*992a56e4SMaxime Ripard  * rate = (parent_rate >> p) / (m + 1);
26*992a56e4SMaxime Ripard  */
27*992a56e4SMaxime Ripard 
28*992a56e4SMaxime Ripard static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate,
29*992a56e4SMaxime Ripard 				       u8 *n, u8 *k, u8 *m, u8 *p)
30*992a56e4SMaxime Ripard {
31*992a56e4SMaxime Ripard 	u8 div, calcm, calcp;
32*992a56e4SMaxime Ripard 
33*992a56e4SMaxime Ripard 	/* These clocks can only divide, so we will never be able to achieve
34*992a56e4SMaxime Ripard 	 * frequencies higher than the parent frequency */
35*992a56e4SMaxime Ripard 	if (*freq > parent_rate)
36*992a56e4SMaxime Ripard 		*freq = parent_rate;
37*992a56e4SMaxime Ripard 
38*992a56e4SMaxime Ripard 	div = DIV_ROUND_UP(parent_rate, *freq);
39*992a56e4SMaxime Ripard 
40*992a56e4SMaxime Ripard 	if (div < 16)
41*992a56e4SMaxime Ripard 		calcp = 0;
42*992a56e4SMaxime Ripard 	else if (div / 2 < 16)
43*992a56e4SMaxime Ripard 		calcp = 1;
44*992a56e4SMaxime Ripard 	else if (div / 4 < 16)
45*992a56e4SMaxime Ripard 		calcp = 2;
46*992a56e4SMaxime Ripard 	else
47*992a56e4SMaxime Ripard 		calcp = 3;
48*992a56e4SMaxime Ripard 
49*992a56e4SMaxime Ripard 	calcm = DIV_ROUND_UP(div, 1 << calcp);
50*992a56e4SMaxime Ripard 
51*992a56e4SMaxime Ripard 	*freq = (parent_rate >> calcp) / calcm;
52*992a56e4SMaxime Ripard 
53*992a56e4SMaxime Ripard 	/* we were called to round the frequency, we can now return */
54*992a56e4SMaxime Ripard 	if (n == NULL)
55*992a56e4SMaxime Ripard 		return;
56*992a56e4SMaxime Ripard 
57*992a56e4SMaxime Ripard 	*m = calcm - 1;
58*992a56e4SMaxime Ripard 	*p = calcp;
59*992a56e4SMaxime Ripard }
60*992a56e4SMaxime Ripard 
61*992a56e4SMaxime Ripard /* user manual says "n" but it's really "p" */
62*992a56e4SMaxime Ripard static struct clk_factors_config sun4i_a10_mod0_config = {
63*992a56e4SMaxime Ripard 	.mshift = 0,
64*992a56e4SMaxime Ripard 	.mwidth = 4,
65*992a56e4SMaxime Ripard 	.pshift = 16,
66*992a56e4SMaxime Ripard 	.pwidth = 2,
67*992a56e4SMaxime Ripard };
68*992a56e4SMaxime Ripard 
69*992a56e4SMaxime Ripard static const struct factors_data sun4i_a10_mod0_data __initconst = {
70*992a56e4SMaxime Ripard 	.enable = 31,
71*992a56e4SMaxime Ripard 	.mux = 24,
72*992a56e4SMaxime Ripard 	.table = &sun4i_a10_mod0_config,
73*992a56e4SMaxime Ripard 	.getter = sun4i_a10_get_mod0_factors,
74*992a56e4SMaxime Ripard };
75*992a56e4SMaxime Ripard 
76*992a56e4SMaxime Ripard static DEFINE_SPINLOCK(sun4i_a10_mod0_lock);
77*992a56e4SMaxime Ripard 
78*992a56e4SMaxime Ripard static void __init sun4i_a10_mod0_setup(struct device_node *node)
79*992a56e4SMaxime Ripard {
80*992a56e4SMaxime Ripard 	sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock);
81*992a56e4SMaxime Ripard }
82*992a56e4SMaxime Ripard CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
83