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