1/* 2 * Copyright (C) 2013 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#define to_clk_div(_hw) container_of(_hw, struct clk_divider, hw) 19#define div_mask(d) ((1 << (d->width)) - 1) 20 21/** 22 * struct clk_fixup_div - imx integer fixup divider clock 23 * @divider: the parent class 24 * @ops: pointer to clk_ops of parent class 25 * @fixup: a hook to fixup the write value 26 * 27 * The imx fixup divider clock is a subclass of basic clk_divider 28 * with an addtional fixup hook. 29 */ 30struct clk_fixup_div { 31 struct clk_divider divider; 32 const struct clk_ops *ops; 33 void (*fixup)(u32 *val); 34}; 35 36static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw) 37{ 38 struct clk_divider *divider = to_clk_div(hw); 39 40 return container_of(divider, struct clk_fixup_div, divider); 41} 42 43static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw, 44 unsigned long parent_rate) 45{ 46 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); 47 48 return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate); 49} 50 51static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate, 52 unsigned long *prate) 53{ 54 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); 55 56 return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate); 57} 58 59static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate, 60 unsigned long parent_rate) 61{ 62 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); 63 struct clk_divider *div = to_clk_div(hw); 64 unsigned int divider, value; 65 unsigned long flags = 0; 66 u32 val; 67 68 divider = parent_rate / rate; 69 70 /* Zero based divider */ 71 value = divider - 1; 72 73 if (value > div_mask(div)) 74 value = div_mask(div); 75 76 spin_lock_irqsave(div->lock, flags); 77 78 val = readl(div->reg); 79 val &= ~(div_mask(div) << div->shift); 80 val |= value << div->shift; 81 fixup_div->fixup(&val); 82 writel(val, div->reg); 83 84 spin_unlock_irqrestore(div->lock, flags); 85 86 return 0; 87} 88 89static const struct clk_ops clk_fixup_div_ops = { 90 .recalc_rate = clk_fixup_div_recalc_rate, 91 .round_rate = clk_fixup_div_round_rate, 92 .set_rate = clk_fixup_div_set_rate, 93}; 94 95struct clk *imx_clk_fixup_divider(const char *name, const char *parent, 96 void __iomem *reg, u8 shift, u8 width, 97 void (*fixup)(u32 *val)) 98{ 99 struct clk_fixup_div *fixup_div; 100 struct clk *clk; 101 struct clk_init_data init; 102 103 if (!fixup) 104 return ERR_PTR(-EINVAL); 105 106 fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL); 107 if (!fixup_div) 108 return ERR_PTR(-ENOMEM); 109 110 init.name = name; 111 init.ops = &clk_fixup_div_ops; 112 init.flags = CLK_SET_RATE_PARENT; 113 init.parent_names = parent ? &parent : NULL; 114 init.num_parents = parent ? 1 : 0; 115 116 fixup_div->divider.reg = reg; 117 fixup_div->divider.shift = shift; 118 fixup_div->divider.width = width; 119 fixup_div->divider.lock = &imx_ccm_lock; 120 fixup_div->divider.hw.init = &init; 121 fixup_div->ops = &clk_divider_ops; 122 fixup_div->fixup = fixup; 123 124 clk = clk_register(NULL, &fixup_div->divider.hw); 125 if (IS_ERR(clk)) 126 kfree(fixup_div); 127 128 return clk; 129} 130