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