[PATCH v3 1/5] clk: Add a basic multiplier clock
Stephen Boyd
sboyd at codeaurora.org
Fri Oct 2 13:43:08 PDT 2015
On 09/29, Maxime Ripard wrote:
> diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c
> new file mode 100644
> index 000000000000..61097e365d55
> --- /dev/null
> +++ b/drivers/clk/clk-multiplier.c
> @@ -0,0 +1,176 @@
> +/*
> + * Copyright (C) 2015 Maxime Ripard <maxime.ripard at free-electrons.com>
> + *
> + * 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 <linux/module.h>
Maybe export.h is more appropriate?
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/of.h>
> +
> +#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw)
> +
> +static unsigned long __get_mult(struct clk_multiplier *mult,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST)
> + return DIV_ROUND_CLOSEST(rate, parent_rate);
We should include kernel.h for this macro.
> +
> + return rate / parent_rate;
> +}
> +
> +static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct clk_multiplier *mult = to_clk_multiplier(hw);
> + unsigned long val;
> +
> + val = clk_readl(mult->reg) >> mult->shift;
> + val &= GENMASK(mult->width, 0);
and bitops.h for GENMASK
> +
> + if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
> + val = 1;
> +
> + return parent_rate * val;
> +}
> +
> +static bool __is_best_rate(unsigned long rate, unsigned long new,
> + unsigned long best, unsigned long flags)
> +{
> + if (flags & CLK_MULTIPLIER_ROUND_CLOSEST)
Is the only difference in this function vs the divider one that
flag? Maybe we should make this function generic to the framework
and pass a flag indicating closest or not.
> + return abs(rate - new) < abs(rate - best);
> +
> + return new >= rate && new < best;
> +}
> +
> +static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
> + unsigned long *best_parent_rate,
> + u8 width, unsigned long flags)
> +{
> + unsigned long orig_parent_rate = *best_parent_rate;
> + unsigned long parent_rate, current_rate, best_rate = ~0;
> + unsigned int i, bestmult = 0;
> +
> + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT))
Please use clk_hw_get_flags. I'd *really* like to kill
__clk_get_flags() but we still have one user in omap code.
> + return rate / *best_parent_rate;
> +
> + for (i = 1; i < ((1 << width) - 1); i++) {
> + if (rate * i == orig_parent_rate) {
> + /*
> + * This is the best case for us if we have a
> + * perfect match without changing the parent
> + * rate.
> + */
> + *best_parent_rate = orig_parent_rate;
> + return i;
> + }
> +
> + parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
> + rate / i);
> + current_rate = parent_rate * i;
> +
> + if (__is_best_rate(rate, current_rate, best_rate, flags)) {
> + bestmult = i;
> + best_rate = current_rate;
> + *best_parent_rate = parent_rate;
> + }
> + }
> +
> + return bestmult;
> +}
> +
> +static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_multiplier *mult = to_clk_multiplier(hw);
> + unsigned long factor = __get_mult(mult, rate, parent_rate);
> + unsigned long uninitialized_var(flags);
hmmm ok. We set it to 0 in other drivers.
> + unsigned long val;
> +
> + if (mult->lock)
> + spin_lock_irqsave(mult->lock, flags);
This needs the same "trick" that we did in the generic clock
types to avoid sparse warnings.
> +
> + val = clk_readl(mult->reg);
> + val &= ~GENMASK(mult->width + mult->shift, mult->shift);
> + val |= factor << mult->shift;
> + clk_writel(val, mult->reg);
> +
> + if (mult->lock)
> + spin_unlock_irqrestore(mult->lock, flags);
> +
> + return 0;
> +}
> +
> +struct clk *clk_register_multiplier(struct device *dev, const char *name,
> + const char *parent_name,
> + unsigned long flags,
> + void __iomem *reg, u8 shift, u8 width,
> + u8 clk_mult_flags, spinlock_t *lock)
> +{
> + struct clk_init_data init;
> + struct clk_multiplier *mult;
> + struct clk *clk;
> +
> + mult = kmalloc(sizeof(*mult), GFP_KERNEL);
> + if (!mult) {
> + pr_err("%s: could not allocate multiplier clk\n", __func__);
We don't need allocation error messages.
> + return ERR_PTR(-ENOMEM);
> + }
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
More information about the linux-arm-kernel
mailing list