xref: /openbmc/linux/drivers/clk/clk-divider.c (revision ce932d0c5589e9766e089c22c66890dfc48fbd94)
1 /*
2  * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
3  * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
4  * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Adjustable divider clock implementation
11  */
12 
13 #include <linux/clk-provider.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/io.h>
17 #include <linux/err.h>
18 #include <linux/string.h>
19 
20 /*
21  * DOC: basic adjustable divider clock that cannot gate
22  *
23  * Traits of this clock:
24  * prepare - clk_prepare only ensures that parents are prepared
25  * enable - clk_enable only ensures that parents are enabled
26  * rate - rate is adjustable.  clk->rate = parent->rate / divisor
27  * parent - fixed parent.  No clk_set_parent support
28  */
29 
30 #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
31 
32 #define div_mask(d)	((1 << (d->width)) - 1)
33 
34 static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
35 		unsigned long parent_rate)
36 {
37 	struct clk_divider *divider = to_clk_divider(hw);
38 	unsigned int div;
39 
40 	div = readl(divider->reg) >> divider->shift;
41 	div &= div_mask(divider);
42 
43 	if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
44 		div++;
45 
46 	return parent_rate / div;
47 }
48 EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
49 
50 /*
51  * The reverse of DIV_ROUND_UP: The maximum number which
52  * divided by m is r
53  */
54 #define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
55 
56 static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
57 		unsigned long *best_parent_rate)
58 {
59 	struct clk_divider *divider = to_clk_divider(hw);
60 	int i, bestdiv = 0;
61 	unsigned long parent_rate, best = 0, now, maxdiv;
62 
63 	if (!rate)
64 		rate = 1;
65 
66 	maxdiv = (1 << divider->width);
67 
68 	if (divider->flags & CLK_DIVIDER_ONE_BASED)
69 		maxdiv--;
70 
71 	if (!best_parent_rate) {
72 		parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
73 		bestdiv = DIV_ROUND_UP(parent_rate, rate);
74 		bestdiv = bestdiv == 0 ? 1 : bestdiv;
75 		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
76 		return bestdiv;
77 	}
78 
79 	/*
80 	 * The maximum divider we can use without overflowing
81 	 * unsigned long in rate * i below
82 	 */
83 	maxdiv = min(ULONG_MAX / rate, maxdiv);
84 
85 	for (i = 1; i <= maxdiv; i++) {
86 		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
87 				MULT_ROUND_UP(rate, i));
88 		now = parent_rate / i;
89 		if (now <= rate && now > best) {
90 			bestdiv = i;
91 			best = now;
92 			*best_parent_rate = parent_rate;
93 		}
94 	}
95 
96 	if (!bestdiv) {
97 		bestdiv = (1 << divider->width);
98 		if (divider->flags & CLK_DIVIDER_ONE_BASED)
99 			bestdiv--;
100 		*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
101 	}
102 
103 	return bestdiv;
104 }
105 
106 static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
107 				unsigned long *prate)
108 {
109 	int div;
110 	div = clk_divider_bestdiv(hw, rate, prate);
111 
112 	if (prate)
113 		return *prate / div;
114 	else {
115 		unsigned long r;
116 		r = __clk_get_rate(__clk_get_parent(hw->clk));
117 		return r / div;
118 	}
119 }
120 EXPORT_SYMBOL_GPL(clk_divider_round_rate);
121 
122 static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate)
123 {
124 	struct clk_divider *divider = to_clk_divider(hw);
125 	unsigned int div;
126 	unsigned long flags = 0;
127 	u32 val;
128 
129 	div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate;
130 
131 	if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
132 		div--;
133 
134 	if (div > div_mask(divider))
135 		div = div_mask(divider);
136 
137 	if (divider->lock)
138 		spin_lock_irqsave(divider->lock, flags);
139 
140 	val = readl(divider->reg);
141 	val &= ~(div_mask(divider) << divider->shift);
142 	val |= div << divider->shift;
143 	writel(val, divider->reg);
144 
145 	if (divider->lock)
146 		spin_unlock_irqrestore(divider->lock, flags);
147 
148 	return 0;
149 }
150 EXPORT_SYMBOL_GPL(clk_divider_set_rate);
151 
152 struct clk_ops clk_divider_ops = {
153 	.recalc_rate = clk_divider_recalc_rate,
154 	.round_rate = clk_divider_round_rate,
155 	.set_rate = clk_divider_set_rate,
156 };
157 EXPORT_SYMBOL_GPL(clk_divider_ops);
158 
159 struct clk *clk_register_divider(struct device *dev, const char *name,
160 		const char *parent_name, unsigned long flags,
161 		void __iomem *reg, u8 shift, u8 width,
162 		u8 clk_divider_flags, spinlock_t *lock)
163 {
164 	struct clk_divider *div;
165 	struct clk *clk;
166 
167 	div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
168 
169 	if (!div) {
170 		pr_err("%s: could not allocate divider clk\n", __func__);
171 		return NULL;
172 	}
173 
174 	/* struct clk_divider assignments */
175 	div->reg = reg;
176 	div->shift = shift;
177 	div->width = width;
178 	div->flags = clk_divider_flags;
179 	div->lock = lock;
180 
181 	if (parent_name) {
182 		div->parent[0] = kstrdup(parent_name, GFP_KERNEL);
183 		if (!div->parent[0])
184 			goto out;
185 	}
186 
187 	clk = clk_register(dev, name,
188 			&clk_divider_ops, &div->hw,
189 			div->parent,
190 			(parent_name ? 1 : 0),
191 			flags);
192 	if (clk)
193 		return clk;
194 
195 out:
196 	kfree(div->parent[0]);
197 	kfree(div);
198 
199 	return NULL;
200 }
201