1 /* 2 * Copyright 2012 Freescale Semiconductor, Inc. 3 * 4 * The code contained herein is licensed under the GNU General Public 5 * License. You may obtain a copy of the GNU General Public License 6 * Version 2 or later at the following locations: 7 * 8 * http://www.opensource.org/licenses/gpl-license.html 9 * http://www.gnu.org/copyleft/gpl.html 10 */ 11 12 #include <linux/clk-provider.h> 13 #include <linux/err.h> 14 #include <linux/io.h> 15 #include <linux/slab.h> 16 #include "clk.h" 17 18 /** 19 * struct clk_frac - mxs fractional divider clock 20 * @hw: clk_hw for the fractional divider clock 21 * @reg: register address 22 * @shift: the divider bit shift 23 * @width: the divider bit width 24 * @busy: busy bit shift 25 * 26 * The clock is an adjustable fractional divider with a busy bit to wait 27 * when the divider is adjusted. 28 */ 29 struct clk_frac { 30 struct clk_hw hw; 31 void __iomem *reg; 32 u8 shift; 33 u8 width; 34 u8 busy; 35 }; 36 37 #define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) 38 39 static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, 40 unsigned long parent_rate) 41 { 42 struct clk_frac *frac = to_clk_frac(hw); 43 u32 div; 44 45 div = readl_relaxed(frac->reg) >> frac->shift; 46 div &= (1 << frac->width) - 1; 47 48 return (parent_rate >> frac->width) * div; 49 } 50 51 static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, 52 unsigned long *prate) 53 { 54 struct clk_frac *frac = to_clk_frac(hw); 55 unsigned long parent_rate = *prate; 56 u32 div; 57 u64 tmp; 58 59 if (rate > parent_rate) 60 return -EINVAL; 61 62 tmp = rate; 63 tmp <<= frac->width; 64 do_div(tmp, parent_rate); 65 div = tmp; 66 67 if (!div) 68 return -EINVAL; 69 70 return (parent_rate >> frac->width) * div; 71 } 72 73 static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, 74 unsigned long parent_rate) 75 { 76 struct clk_frac *frac = to_clk_frac(hw); 77 unsigned long flags; 78 u32 div, val; 79 u64 tmp; 80 81 if (rate > parent_rate) 82 return -EINVAL; 83 84 tmp = rate; 85 tmp <<= frac->width; 86 do_div(tmp, parent_rate); 87 div = tmp; 88 89 if (!div) 90 return -EINVAL; 91 92 spin_lock_irqsave(&mxs_lock, flags); 93 94 val = readl_relaxed(frac->reg); 95 val &= ~(((1 << frac->width) - 1) << frac->shift); 96 val |= div << frac->shift; 97 writel_relaxed(val, frac->reg); 98 99 spin_unlock_irqrestore(&mxs_lock, flags); 100 101 return mxs_clk_wait(frac->reg, frac->busy); 102 } 103 104 static struct clk_ops clk_frac_ops = { 105 .recalc_rate = clk_frac_recalc_rate, 106 .round_rate = clk_frac_round_rate, 107 .set_rate = clk_frac_set_rate, 108 }; 109 110 struct clk *mxs_clk_frac(const char *name, const char *parent_name, 111 void __iomem *reg, u8 shift, u8 width, u8 busy) 112 { 113 struct clk_frac *frac; 114 struct clk *clk; 115 struct clk_init_data init; 116 117 frac = kzalloc(sizeof(*frac), GFP_KERNEL); 118 if (!frac) 119 return ERR_PTR(-ENOMEM); 120 121 init.name = name; 122 init.ops = &clk_frac_ops; 123 init.flags = CLK_SET_RATE_PARENT; 124 init.parent_names = (parent_name ? &parent_name: NULL); 125 init.num_parents = (parent_name ? 1 : 0); 126 127 frac->reg = reg; 128 frac->shift = shift; 129 frac->width = width; 130 frac->busy = busy; 131 frac->hw.init = &init; 132 133 clk = clk_register(NULL, &frac->hw); 134 if (IS_ERR(clk)) 135 kfree(frac); 136 137 return clk; 138 } 139