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