From e09b41010ba33a20a87472ee821fa407a5b8da36 Mon Sep 17 00:00:00 2001 From: José Pekkarinen Date: Mon, 11 Apr 2016 10:41:07 +0300 Subject: These changes are the raw update to linux-4.4.6-rt14. Kernel sources are taken from kernel.org, and rt patch from the rt wiki download page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen --- kernel/drivers/clk/hisilicon/clkdivider-hi6220.c | 156 +++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 kernel/drivers/clk/hisilicon/clkdivider-hi6220.c (limited to 'kernel/drivers/clk/hisilicon/clkdivider-hi6220.c') diff --git a/kernel/drivers/clk/hisilicon/clkdivider-hi6220.c b/kernel/drivers/clk/hisilicon/clkdivider-hi6220.c new file mode 100644 index 000000000..113eee8ed --- /dev/null +++ b/kernel/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,156 @@ +/* + * Hisilicon hi6220 SoC divider clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#define div_mask(width) ((1 << (width)) - 1) + +/** + * struct hi6220_clk_divider - divider clock for hi6220 + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @mask: mask for setting divider rate + * @table: the div table that the divider supports + * @lock: register lock + */ +struct hi6220_clk_divider { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u32 mask; + const struct clk_div_table *table; + spinlock_t *lock; +}; + +#define to_hi6220_clk_divider(_hw) \ + container_of(_hw, struct hi6220_clk_divider, hw) + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + return divider_recalc_rate(hw, parent_rate, val, dclk->table, + CLK_DIVIDER_ROUND_CLOSEST); +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + return divider_round_rate(hw, rate, prate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + value = divider_get_val(rate, parent_rate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(div_mask(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mask; + + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static const struct clk_ops hi6220_clkdiv_ops = { + .recalc_rate = hi6220_clkdiv_recalc_rate, + .round_rate = hi6220_clkdiv_round_rate, + .set_rate = hi6220_clkdiv_set_rate, +}; + +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) +{ + struct hi6220_clk_divider *div; + struct clk *clk; + struct clk_init_data init; + struct clk_div_table *table; + u32 max_div, min_div; + int i; + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < max_div; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 1; + } + + init.name = name; + init.ops = &hi6220_clkdiv_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + /* struct hi6220_clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->mask = mask_bit ? BIT(mask_bit) : 0; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + clk = clk_register(dev, &div->hw); + if (IS_ERR(clk)) { + kfree(table); + kfree(div); + } + + return clk; +} -- cgit 1.2.3-korg