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