0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/clk-provider.h>
0012 #include <linux/slab.h>
0013 #include <linux/io.h>
0014 #include <linux/err.h>
0015 #include <linux/spinlock.h>
0016
0017 #include "clk.h"
0018
0019 #define div_mask(width) ((1 << (width)) - 1)
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 struct hi6220_clk_divider {
0033 struct clk_hw hw;
0034 void __iomem *reg;
0035 u8 shift;
0036 u8 width;
0037 u32 mask;
0038 const struct clk_div_table *table;
0039 spinlock_t *lock;
0040 };
0041
0042 #define to_hi6220_clk_divider(_hw) \
0043 container_of(_hw, struct hi6220_clk_divider, hw)
0044
0045 static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
0046 unsigned long parent_rate)
0047 {
0048 unsigned int val;
0049 struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
0050
0051 val = readl_relaxed(dclk->reg) >> dclk->shift;
0052 val &= div_mask(dclk->width);
0053
0054 return divider_recalc_rate(hw, parent_rate, val, dclk->table,
0055 CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
0056 }
0057
0058 static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
0059 unsigned long *prate)
0060 {
0061 struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
0062
0063 return divider_round_rate(hw, rate, prate, dclk->table,
0064 dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
0065 }
0066
0067 static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
0068 unsigned long parent_rate)
0069 {
0070 int value;
0071 unsigned long flags = 0;
0072 u32 data;
0073 struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
0074
0075 value = divider_get_val(rate, parent_rate, dclk->table,
0076 dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
0077
0078 if (dclk->lock)
0079 spin_lock_irqsave(dclk->lock, flags);
0080
0081 data = readl_relaxed(dclk->reg);
0082 data &= ~(div_mask(dclk->width) << dclk->shift);
0083 data |= value << dclk->shift;
0084 data |= dclk->mask;
0085
0086 writel_relaxed(data, dclk->reg);
0087
0088 if (dclk->lock)
0089 spin_unlock_irqrestore(dclk->lock, flags);
0090
0091 return 0;
0092 }
0093
0094 static const struct clk_ops hi6220_clkdiv_ops = {
0095 .recalc_rate = hi6220_clkdiv_recalc_rate,
0096 .round_rate = hi6220_clkdiv_round_rate,
0097 .set_rate = hi6220_clkdiv_set_rate,
0098 };
0099
0100 struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
0101 const char *parent_name, unsigned long flags, void __iomem *reg,
0102 u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
0103 {
0104 struct hi6220_clk_divider *div;
0105 struct clk *clk;
0106 struct clk_init_data init;
0107 struct clk_div_table *table;
0108 u32 max_div, min_div;
0109 int i;
0110
0111
0112 div = kzalloc(sizeof(*div), GFP_KERNEL);
0113 if (!div)
0114 return ERR_PTR(-ENOMEM);
0115
0116
0117 max_div = div_mask(width) + 1;
0118 min_div = 1;
0119
0120 table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
0121 if (!table) {
0122 kfree(div);
0123 return ERR_PTR(-ENOMEM);
0124 }
0125
0126 for (i = 0; i < max_div; i++) {
0127 table[i].div = min_div + i;
0128 table[i].val = table[i].div - 1;
0129 }
0130
0131 init.name = name;
0132 init.ops = &hi6220_clkdiv_ops;
0133 init.flags = flags;
0134 init.parent_names = parent_name ? &parent_name : NULL;
0135 init.num_parents = parent_name ? 1 : 0;
0136
0137
0138 div->reg = reg;
0139 div->shift = shift;
0140 div->width = width;
0141 div->mask = mask_bit ? BIT(mask_bit) : 0;
0142 div->lock = lock;
0143 div->hw.init = &init;
0144 div->table = table;
0145
0146
0147 clk = clk_register(dev, &div->hw);
0148 if (IS_ERR(clk)) {
0149 kfree(table);
0150 kfree(div);
0151 }
0152
0153 return clk;
0154 }