xref: /openbmc/linux/drivers/clk/clk-divider.c (revision 1c0035d710dd3bfa86d58f851b8737c7f11a9bbc)
19d9f78edSMike Turquette /*
29d9f78edSMike Turquette  * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
39d9f78edSMike Turquette  * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
49d9f78edSMike Turquette  * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
59d9f78edSMike Turquette  *
69d9f78edSMike Turquette  * This program is free software; you can redistribute it and/or modify
79d9f78edSMike Turquette  * it under the terms of the GNU General Public License version 2 as
89d9f78edSMike Turquette  * published by the Free Software Foundation.
99d9f78edSMike Turquette  *
109d9f78edSMike Turquette  * Adjustable divider clock implementation
119d9f78edSMike Turquette  */
129d9f78edSMike Turquette 
139d9f78edSMike Turquette #include <linux/clk-provider.h>
149d9f78edSMike Turquette #include <linux/module.h>
159d9f78edSMike Turquette #include <linux/slab.h>
169d9f78edSMike Turquette #include <linux/io.h>
179d9f78edSMike Turquette #include <linux/err.h>
189d9f78edSMike Turquette #include <linux/string.h>
199d9f78edSMike Turquette 
209d9f78edSMike Turquette /*
219d9f78edSMike Turquette  * DOC: basic adjustable divider clock that cannot gate
229d9f78edSMike Turquette  *
239d9f78edSMike Turquette  * Traits of this clock:
249d9f78edSMike Turquette  * prepare - clk_prepare only ensures that parents are prepared
259d9f78edSMike Turquette  * enable - clk_enable only ensures that parents are enabled
269d9f78edSMike Turquette  * rate - rate is adjustable.  clk->rate = parent->rate / divisor
279d9f78edSMike Turquette  * parent - fixed parent.  No clk_set_parent support
289d9f78edSMike Turquette  */
299d9f78edSMike Turquette 
309d9f78edSMike Turquette #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
319d9f78edSMike Turquette 
329d9f78edSMike Turquette #define div_mask(d)	((1 << (d->width)) - 1)
339d9f78edSMike Turquette 
349d9f78edSMike Turquette static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
359d9f78edSMike Turquette 		unsigned long parent_rate)
369d9f78edSMike Turquette {
379d9f78edSMike Turquette 	struct clk_divider *divider = to_clk_divider(hw);
389d9f78edSMike Turquette 	unsigned int div;
399d9f78edSMike Turquette 
409d9f78edSMike Turquette 	div = readl(divider->reg) >> divider->shift;
419d9f78edSMike Turquette 	div &= div_mask(divider);
429d9f78edSMike Turquette 
439d9f78edSMike Turquette 	if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
449d9f78edSMike Turquette 		div++;
459d9f78edSMike Turquette 
469d9f78edSMike Turquette 	return parent_rate / div;
479d9f78edSMike Turquette }
489d9f78edSMike Turquette 
499d9f78edSMike Turquette /*
509d9f78edSMike Turquette  * The reverse of DIV_ROUND_UP: The maximum number which
519d9f78edSMike Turquette  * divided by m is r
529d9f78edSMike Turquette  */
539d9f78edSMike Turquette #define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
549d9f78edSMike Turquette 
559d9f78edSMike Turquette static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
569d9f78edSMike Turquette 		unsigned long *best_parent_rate)
579d9f78edSMike Turquette {
589d9f78edSMike Turquette 	struct clk_divider *divider = to_clk_divider(hw);
599d9f78edSMike Turquette 	int i, bestdiv = 0;
609d9f78edSMike Turquette 	unsigned long parent_rate, best = 0, now, maxdiv;
619d9f78edSMike Turquette 
629d9f78edSMike Turquette 	if (!rate)
639d9f78edSMike Turquette 		rate = 1;
649d9f78edSMike Turquette 
659d9f78edSMike Turquette 	maxdiv = (1 << divider->width);
669d9f78edSMike Turquette 
679d9f78edSMike Turquette 	if (divider->flags & CLK_DIVIDER_ONE_BASED)
689d9f78edSMike Turquette 		maxdiv--;
699d9f78edSMike Turquette 
7081536e07SShawn Guo 	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
7181536e07SShawn Guo 		parent_rate = *best_parent_rate;
729d9f78edSMike Turquette 		bestdiv = DIV_ROUND_UP(parent_rate, rate);
739d9f78edSMike Turquette 		bestdiv = bestdiv == 0 ? 1 : bestdiv;
749d9f78edSMike Turquette 		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
759d9f78edSMike Turquette 		return bestdiv;
769d9f78edSMike Turquette 	}
779d9f78edSMike Turquette 
789d9f78edSMike Turquette 	/*
799d9f78edSMike Turquette 	 * The maximum divider we can use without overflowing
809d9f78edSMike Turquette 	 * unsigned long in rate * i below
819d9f78edSMike Turquette 	 */
829d9f78edSMike Turquette 	maxdiv = min(ULONG_MAX / rate, maxdiv);
839d9f78edSMike Turquette 
849d9f78edSMike Turquette 	for (i = 1; i <= maxdiv; i++) {
859d9f78edSMike Turquette 		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
869d9f78edSMike Turquette 				MULT_ROUND_UP(rate, i));
879d9f78edSMike Turquette 		now = parent_rate / i;
889d9f78edSMike Turquette 		if (now <= rate && now > best) {
899d9f78edSMike Turquette 			bestdiv = i;
909d9f78edSMike Turquette 			best = now;
919d9f78edSMike Turquette 			*best_parent_rate = parent_rate;
929d9f78edSMike Turquette 		}
939d9f78edSMike Turquette 	}
949d9f78edSMike Turquette 
959d9f78edSMike Turquette 	if (!bestdiv) {
969d9f78edSMike Turquette 		bestdiv = (1 << divider->width);
979d9f78edSMike Turquette 		if (divider->flags & CLK_DIVIDER_ONE_BASED)
989d9f78edSMike Turquette 			bestdiv--;
999d9f78edSMike Turquette 		*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
1009d9f78edSMike Turquette 	}
1019d9f78edSMike Turquette 
1029d9f78edSMike Turquette 	return bestdiv;
1039d9f78edSMike Turquette }
1049d9f78edSMike Turquette 
1059d9f78edSMike Turquette static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
1069d9f78edSMike Turquette 				unsigned long *prate)
1079d9f78edSMike Turquette {
1089d9f78edSMike Turquette 	int div;
1099d9f78edSMike Turquette 	div = clk_divider_bestdiv(hw, rate, prate);
1109d9f78edSMike Turquette 
1119d9f78edSMike Turquette 	return *prate / div;
1129d9f78edSMike Turquette }
1139d9f78edSMike Turquette 
114*1c0035d7SShawn Guo static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
115*1c0035d7SShawn Guo 				unsigned long parent_rate)
1169d9f78edSMike Turquette {
1179d9f78edSMike Turquette 	struct clk_divider *divider = to_clk_divider(hw);
1189d9f78edSMike Turquette 	unsigned int div;
1199d9f78edSMike Turquette 	unsigned long flags = 0;
1209d9f78edSMike Turquette 	u32 val;
1219d9f78edSMike Turquette 
122*1c0035d7SShawn Guo 	div = parent_rate / rate;
1239d9f78edSMike Turquette 
1249d9f78edSMike Turquette 	if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
1259d9f78edSMike Turquette 		div--;
1269d9f78edSMike Turquette 
1279d9f78edSMike Turquette 	if (div > div_mask(divider))
1289d9f78edSMike Turquette 		div = div_mask(divider);
1299d9f78edSMike Turquette 
1309d9f78edSMike Turquette 	if (divider->lock)
1319d9f78edSMike Turquette 		spin_lock_irqsave(divider->lock, flags);
1329d9f78edSMike Turquette 
1339d9f78edSMike Turquette 	val = readl(divider->reg);
1349d9f78edSMike Turquette 	val &= ~(div_mask(divider) << divider->shift);
1359d9f78edSMike Turquette 	val |= div << divider->shift;
1369d9f78edSMike Turquette 	writel(val, divider->reg);
1379d9f78edSMike Turquette 
1389d9f78edSMike Turquette 	if (divider->lock)
1399d9f78edSMike Turquette 		spin_unlock_irqrestore(divider->lock, flags);
1409d9f78edSMike Turquette 
1419d9f78edSMike Turquette 	return 0;
1429d9f78edSMike Turquette }
1439d9f78edSMike Turquette 
144822c250eSShawn Guo const struct clk_ops clk_divider_ops = {
1459d9f78edSMike Turquette 	.recalc_rate = clk_divider_recalc_rate,
1469d9f78edSMike Turquette 	.round_rate = clk_divider_round_rate,
1479d9f78edSMike Turquette 	.set_rate = clk_divider_set_rate,
1489d9f78edSMike Turquette };
1499d9f78edSMike Turquette EXPORT_SYMBOL_GPL(clk_divider_ops);
1509d9f78edSMike Turquette 
15127d54591SMike Turquette /**
15227d54591SMike Turquette  * clk_register_divider - register a divider clock with the clock framework
15327d54591SMike Turquette  * @dev: device registering this clock
15427d54591SMike Turquette  * @name: name of this clock
15527d54591SMike Turquette  * @parent_name: name of clock's parent
15627d54591SMike Turquette  * @flags: framework-specific flags
15727d54591SMike Turquette  * @reg: register address to adjust divider
15827d54591SMike Turquette  * @shift: number of bits to shift the bitfield
15927d54591SMike Turquette  * @width: width of the bitfield
16027d54591SMike Turquette  * @clk_divider_flags: divider-specific flags for this clock
16127d54591SMike Turquette  * @lock: shared register lock for this clock
16227d54591SMike Turquette  */
1639d9f78edSMike Turquette struct clk *clk_register_divider(struct device *dev, const char *name,
1649d9f78edSMike Turquette 		const char *parent_name, unsigned long flags,
1659d9f78edSMike Turquette 		void __iomem *reg, u8 shift, u8 width,
1669d9f78edSMike Turquette 		u8 clk_divider_flags, spinlock_t *lock)
1679d9f78edSMike Turquette {
1689d9f78edSMike Turquette 	struct clk_divider *div;
1699d9f78edSMike Turquette 	struct clk *clk;
1709d9f78edSMike Turquette 
17127d54591SMike Turquette 	/* allocate the divider */
1729d9f78edSMike Turquette 	div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
1739d9f78edSMike Turquette 	if (!div) {
1749d9f78edSMike Turquette 		pr_err("%s: could not allocate divider clk\n", __func__);
17527d54591SMike Turquette 		return ERR_PTR(-ENOMEM);
1769d9f78edSMike Turquette 	}
1779d9f78edSMike Turquette 
1789d9f78edSMike Turquette 	/* struct clk_divider assignments */
1799d9f78edSMike Turquette 	div->reg = reg;
1809d9f78edSMike Turquette 	div->shift = shift;
1819d9f78edSMike Turquette 	div->width = width;
1829d9f78edSMike Turquette 	div->flags = clk_divider_flags;
1839d9f78edSMike Turquette 	div->lock = lock;
1849d9f78edSMike Turquette 
18527d54591SMike Turquette 	/* register the clock */
1869d9f78edSMike Turquette 	clk = clk_register(dev, name,
1879d9f78edSMike Turquette 			&clk_divider_ops, &div->hw,
18827d54591SMike Turquette 			(parent_name ? &parent_name: NULL),
1899d9f78edSMike Turquette 			(parent_name ? 1 : 0),
1909d9f78edSMike Turquette 			flags);
1919d9f78edSMike Turquette 
19227d54591SMike Turquette 	if (IS_ERR(clk))
1939d9f78edSMike Turquette 		kfree(div);
1949d9f78edSMike Turquette 
19527d54591SMike Turquette 	return clk;
1969d9f78edSMike Turquette }
197