xref: /linux/drivers/clk/sunxi/clk-sun6i-ar100.c (revision 3932b9ca55b0be314a36d3e84faff3e823c081f5)
1 /*
2  * Copyright (C) 2014 Free Electrons
3  *
4  * License Terms: GNU General Public License v2
5  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6  *
7  * Allwinner A31 AR100 clock driver
8  *
9  */
10 
11 #include <linux/clk-provider.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15 
16 #define SUN6I_AR100_MAX_PARENTS		4
17 #define SUN6I_AR100_SHIFT_MASK		0x3
18 #define SUN6I_AR100_SHIFT_MAX		SUN6I_AR100_SHIFT_MASK
19 #define SUN6I_AR100_SHIFT_SHIFT		4
20 #define SUN6I_AR100_DIV_MASK		0x1f
21 #define SUN6I_AR100_DIV_MAX		(SUN6I_AR100_DIV_MASK + 1)
22 #define SUN6I_AR100_DIV_SHIFT		8
23 #define SUN6I_AR100_MUX_MASK		0x3
24 #define SUN6I_AR100_MUX_SHIFT		16
25 
26 struct ar100_clk {
27 	struct clk_hw hw;
28 	void __iomem *reg;
29 };
30 
31 static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw)
32 {
33 	return container_of(hw, struct ar100_clk, hw);
34 }
35 
36 static unsigned long ar100_recalc_rate(struct clk_hw *hw,
37 				       unsigned long parent_rate)
38 {
39 	struct ar100_clk *clk = to_ar100_clk(hw);
40 	u32 val = readl(clk->reg);
41 	int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK;
42 	int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK;
43 
44 	return (parent_rate >> shift) / (div + 1);
45 }
46 
47 static long ar100_determine_rate(struct clk_hw *hw, unsigned long rate,
48 				 unsigned long *best_parent_rate,
49 				 struct clk **best_parent_clk)
50 {
51 	int nparents = __clk_get_num_parents(hw->clk);
52 	long best_rate = -EINVAL;
53 	int i;
54 
55 	*best_parent_clk = NULL;
56 
57 	for (i = 0; i < nparents; i++) {
58 		unsigned long parent_rate;
59 		unsigned long tmp_rate;
60 		struct clk *parent;
61 		unsigned long div;
62 		int shift;
63 
64 		parent = clk_get_parent_by_index(hw->clk, i);
65 		parent_rate = __clk_get_rate(parent);
66 		div = DIV_ROUND_UP(parent_rate, rate);
67 
68 		/*
69 		 * The AR100 clk contains 2 divisors:
70 		 * - one power of 2 divisor
71 		 * - one regular divisor
72 		 *
73 		 * First check if we can safely shift (or divide by a power
74 		 * of 2) without losing precision on the requested rate.
75 		 */
76 		shift = ffs(div) - 1;
77 		if (shift > SUN6I_AR100_SHIFT_MAX)
78 			shift = SUN6I_AR100_SHIFT_MAX;
79 
80 		div >>= shift;
81 
82 		/*
83 		 * Then if the divisor is still bigger than what the HW
84 		 * actually supports, use a bigger shift (or power of 2
85 		 * divider) value and accept to lose some precision.
86 		 */
87 		while (div > SUN6I_AR100_DIV_MAX) {
88 			shift++;
89 			div >>= 1;
90 			if (shift > SUN6I_AR100_SHIFT_MAX)
91 				break;
92 		}
93 
94 		/*
95 		 * If the shift value (or power of 2 divider) is bigger
96 		 * than what the HW actually support, skip this parent.
97 		 */
98 		if (shift > SUN6I_AR100_SHIFT_MAX)
99 			continue;
100 
101 		tmp_rate = (parent_rate >> shift) / div;
102 		if (!*best_parent_clk || tmp_rate > best_rate) {
103 			*best_parent_clk = parent;
104 			*best_parent_rate = parent_rate;
105 			best_rate = tmp_rate;
106 		}
107 	}
108 
109 	return best_rate;
110 }
111 
112 static int ar100_set_parent(struct clk_hw *hw, u8 index)
113 {
114 	struct ar100_clk *clk = to_ar100_clk(hw);
115 	u32 val = readl(clk->reg);
116 
117 	if (index >= SUN6I_AR100_MAX_PARENTS)
118 		return -EINVAL;
119 
120 	val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT);
121 	val |= (index << SUN6I_AR100_MUX_SHIFT);
122 	writel(val, clk->reg);
123 
124 	return 0;
125 }
126 
127 static u8 ar100_get_parent(struct clk_hw *hw)
128 {
129 	struct ar100_clk *clk = to_ar100_clk(hw);
130 	return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) &
131 	       SUN6I_AR100_MUX_MASK;
132 }
133 
134 static int ar100_set_rate(struct clk_hw *hw, unsigned long rate,
135 			  unsigned long parent_rate)
136 {
137 	unsigned long div = parent_rate / rate;
138 	struct ar100_clk *clk = to_ar100_clk(hw);
139 	u32 val = readl(clk->reg);
140 	int shift;
141 
142 	if (parent_rate % rate)
143 		return -EINVAL;
144 
145 	shift = ffs(div) - 1;
146 	if (shift > SUN6I_AR100_SHIFT_MAX)
147 		shift = SUN6I_AR100_SHIFT_MAX;
148 
149 	div >>= shift;
150 
151 	if (div > SUN6I_AR100_DIV_MAX)
152 		return -EINVAL;
153 
154 	val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) |
155 		 (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT));
156 	val |= (shift << SUN6I_AR100_SHIFT_SHIFT) |
157 	       (div << SUN6I_AR100_DIV_SHIFT);
158 	writel(val, clk->reg);
159 
160 	return 0;
161 }
162 
163 static struct clk_ops ar100_ops = {
164 	.recalc_rate = ar100_recalc_rate,
165 	.determine_rate = ar100_determine_rate,
166 	.set_parent = ar100_set_parent,
167 	.get_parent = ar100_get_parent,
168 	.set_rate = ar100_set_rate,
169 };
170 
171 static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
172 {
173 	const char *parents[SUN6I_AR100_MAX_PARENTS];
174 	struct device_node *np = pdev->dev.of_node;
175 	const char *clk_name = np->name;
176 	struct clk_init_data init;
177 	struct ar100_clk *ar100;
178 	struct resource *r;
179 	struct clk *clk;
180 	int nparents;
181 	int i;
182 
183 	ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL);
184 	if (!ar100)
185 		return -ENOMEM;
186 
187 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
188 	ar100->reg = devm_ioremap_resource(&pdev->dev, r);
189 	if (IS_ERR(ar100->reg))
190 		return PTR_ERR(ar100->reg);
191 
192 	nparents = of_clk_get_parent_count(np);
193 	if (nparents > SUN6I_AR100_MAX_PARENTS)
194 		nparents = SUN6I_AR100_MAX_PARENTS;
195 
196 	for (i = 0; i < nparents; i++)
197 		parents[i] = of_clk_get_parent_name(np, i);
198 
199 	of_property_read_string(np, "clock-output-names", &clk_name);
200 
201 	init.name = clk_name;
202 	init.ops = &ar100_ops;
203 	init.parent_names = parents;
204 	init.num_parents = nparents;
205 	init.flags = 0;
206 
207 	ar100->hw.init = &init;
208 
209 	clk = clk_register(&pdev->dev, &ar100->hw);
210 	if (IS_ERR(clk))
211 		return PTR_ERR(clk);
212 
213 	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
214 }
215 
216 static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
217 	{ .compatible = "allwinner,sun6i-a31-ar100-clk" },
218 	{ /* sentinel */ }
219 };
220 
221 static struct platform_driver sun6i_a31_ar100_clk_driver = {
222 	.driver = {
223 		.name = "sun6i-a31-ar100-clk",
224 		.owner = THIS_MODULE,
225 		.of_match_table = sun6i_a31_ar100_clk_dt_ids,
226 	},
227 	.probe = sun6i_a31_ar100_clk_probe,
228 };
229 module_platform_driver(sun6i_a31_ar100_clk_driver);
230 
231 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
232 MODULE_DESCRIPTION("Allwinner A31 AR100 clock Driver");
233 MODULE_LICENSE("GPL v2");
234