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.h> 13#include <linux/clk-provider.h> 14#include <linux/err.h> 15#include <linux/io.h> 16#include <linux/slab.h> 17#include "clk.h" 18 19/** 20 * struct clk_ref - mxs reference clock 21 * @hw: clk_hw for the reference clock 22 * @reg: register address 23 * @idx: the index of the reference clock within the same register 24 * 25 * The mxs reference clock sources from pll. Every 4 reference clocks share 26 * one register space, and @idx is used to identify them. Each reference 27 * clock has a gate control and a fractional * divider. The rate is calculated 28 * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35. 29 */ 30struct clk_ref { 31 struct clk_hw hw; 32 void __iomem *reg; 33 u8 idx; 34}; 35 36#define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw) 37 38static int clk_ref_enable(struct clk_hw *hw) 39{ 40 struct clk_ref *ref = to_clk_ref(hw); 41 42 writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); 43 44 return 0; 45} 46 47static void clk_ref_disable(struct clk_hw *hw) 48{ 49 struct clk_ref *ref = to_clk_ref(hw); 50 51 writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); 52} 53 54static unsigned long clk_ref_recalc_rate(struct clk_hw *hw, 55 unsigned long parent_rate) 56{ 57 struct clk_ref *ref = to_clk_ref(hw); 58 u64 tmp = parent_rate; 59 u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f; 60 61 tmp *= 18; 62 do_div(tmp, frac); 63 64 return tmp; 65} 66 67static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate, 68 unsigned long *prate) 69{ 70 unsigned long parent_rate = *prate; 71 u64 tmp = parent_rate; 72 u8 frac; 73 74 tmp = tmp * 18 + rate / 2; 75 do_div(tmp, rate); 76 frac = tmp; 77 78 if (frac < 18) 79 frac = 18; 80 else if (frac > 35) 81 frac = 35; 82 83 tmp = parent_rate; 84 tmp *= 18; 85 do_div(tmp, frac); 86 87 return tmp; 88} 89 90static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, 91 unsigned long parent_rate) 92{ 93 struct clk_ref *ref = to_clk_ref(hw); 94 unsigned long flags; 95 u64 tmp = parent_rate; 96 u32 val; 97 u8 frac, shift = ref->idx * 8; 98 99 tmp = tmp * 18 + rate / 2; 100 do_div(tmp, rate); 101 frac = tmp; 102 103 if (frac < 18) 104 frac = 18; 105 else if (frac > 35) 106 frac = 35; 107 108 spin_lock_irqsave(&mxs_lock, flags); 109 110 val = readl_relaxed(ref->reg); 111 val &= ~(0x3f << shift); 112 val |= frac << shift; 113 writel_relaxed(val, ref->reg); 114 115 spin_unlock_irqrestore(&mxs_lock, flags); 116 117 return 0; 118} 119 120static const struct clk_ops clk_ref_ops = { 121 .enable = clk_ref_enable, 122 .disable = clk_ref_disable, 123 .recalc_rate = clk_ref_recalc_rate, 124 .round_rate = clk_ref_round_rate, 125 .set_rate = clk_ref_set_rate, 126}; 127 128struct clk *mxs_clk_ref(const char *name, const char *parent_name, 129 void __iomem *reg, u8 idx) 130{ 131 struct clk_ref *ref; 132 struct clk *clk; 133 struct clk_init_data init; 134 135 ref = kzalloc(sizeof(*ref), GFP_KERNEL); 136 if (!ref) 137 return ERR_PTR(-ENOMEM); 138 139 init.name = name; 140 init.ops = &clk_ref_ops; 141 init.flags = 0; 142 init.parent_names = (parent_name ? &parent_name: NULL); 143 init.num_parents = (parent_name ? 1 : 0); 144 145 ref->reg = reg; 146 ref->idx = idx; 147 ref->hw.init = &init; 148 149 clk = clk_register(NULL, &ref->hw); 150 if (IS_ERR(clk)) 151 kfree(ref); 152 153 return clk; 154} 155