Re: [PATCHv2 3/4] clk: new basic clk type for fractional divider
From: Mike Turquette
Date: Thu May 15 2014 - 12:54:03 EST
Quoting Heikki Krogerus (2014-05-15 06:40:25)
> Fractional divider clocks are fairly common. This adds basic
> type for them.
>
> Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
Taken into clk-next.
Just FYI, there was some talk at Embedded Linux Conference on providing
a better abstraction layer for some of these "basic" clock types. This
abstraction would allow the basic clock types to implement the
machine-agnostic logic (e.g. an incoming rate is divided by a value) and
then platforms and drivers could plug in the machine-specific parts
(e.g. divider is made up of m/n, or divider is power-of-two, or divider
is a simple integer with min == 1 and max == 5).
All of that is to say that in time this fractional divider could go away
once the abstraction layer allows us to fold the m/n divider stuff into
a core divider implementation.
Nothing wrong with the patch for now, so I've taken it for 3.16.
Regards,
Mike
> ---
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-fractional-divider.c | 135 +++++++++++++++++++++++++++++++++++
> include/linux/clk-provider.h | 31 ++++++++
> 3 files changed, 167 insertions(+)
> create mode 100644 drivers/clk/clk-fractional-divider.c
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f651b2a..33bc79e 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
> obj-$(CONFIG_COMMON_CLK) += clk-gate.o
> obj-$(CONFIG_COMMON_CLK) += clk-mux.o
> obj-$(CONFIG_COMMON_CLK) += clk-composite.o
> +obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
>
> # hardware specific clock types
> # please keep this section sorted lexicographically by file/directory path name
> diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
> new file mode 100644
> index 0000000..ede685c
> --- /dev/null
> +++ b/drivers/clk/clk-fractional-divider.c
> @@ -0,0 +1,135 @@
> +/*
> + * Copyright (C) 2014 Intel Corporation
> + *
> + * 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.
> + *
> + * Adjustable fractional divider clock implementation.
> + * Output rate = (m / n) * parent_rate.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <linux/gcd.h>
> +
> +#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
> +
> +static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct clk_fractional_divider *fd = to_clk_fd(hw);
> + unsigned long flags = 0;
> + u32 val, m, n;
> + u64 ret;
> +
> + if (fd->lock)
> + spin_lock_irqsave(fd->lock, flags);
> +
> + val = clk_readl(fd->reg);
> +
> + if (fd->lock)
> + spin_unlock_irqrestore(fd->lock, flags);
> +
> + m = (val & fd->mmask) >> fd->mshift;
> + n = (val & fd->nmask) >> fd->nshift;
> +
> + ret = parent_rate * m;
> + do_div(ret, n);
> +
> + return ret;
> +}
> +
> +static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct clk_fractional_divider *fd = to_clk_fd(hw);
> + unsigned maxn = (fd->nmask >> fd->nshift) + 1;
> + unsigned div;
> +
> + if (!rate || rate >= *prate)
> + return *prate;
> +
> + div = gcd(*prate, rate);
> +
> + while ((*prate / div) > maxn) {
> + div <<= 1;
> + rate <<= 1;
> + }
> +
> + return rate;
> +}
> +
> +static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_fractional_divider *fd = to_clk_fd(hw);
> + unsigned long flags = 0;
> + unsigned long div;
> + unsigned n, m;
> + u32 val;
> +
> + div = gcd(parent_rate, rate);
> + m = rate / div;
> + n = parent_rate / div;
> +
> + if (fd->lock)
> + spin_lock_irqsave(fd->lock, flags);
> +
> + val = clk_readl(fd->reg);
> + val &= ~(fd->mmask | fd->nmask);
> + val |= (m << fd->mshift) | (n << fd->nshift);
> + clk_writel(val, fd->reg);
> +
> + if (fd->lock)
> + spin_unlock_irqrestore(fd->lock, flags);
> +
> + return 0;
> +}
> +
> +const struct clk_ops clk_fractional_divider_ops = {
> + .recalc_rate = clk_fd_recalc_rate,
> + .round_rate = clk_fd_round_rate,
> + .set_rate = clk_fd_set_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
> +
> +struct clk *clk_register_fractional_divider(struct device *dev,
> + const char *name, const char *parent_name, unsigned long flags,
> + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
> + u8 clk_divider_flags, spinlock_t *lock)
> +{
> + struct clk_fractional_divider *fd;
> + struct clk_init_data init;
> + struct clk *clk;
> +
> + fd = kzalloc(sizeof(*fd), GFP_KERNEL);
> + if (!fd) {
> + dev_err(dev, "could not allocate fractional divider clk\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + init.name = name;
> + init.ops = &clk_fractional_divider_ops;
> + init.flags = flags | CLK_IS_BASIC;
> + init.parent_names = parent_name ? &parent_name : NULL;
> + init.num_parents = parent_name ? 1 : 0;
> +
> + fd->reg = reg;
> + fd->mshift = mshift;
> + fd->mmask = (BIT(mwidth) - 1) << mshift;
> + fd->nshift = nshift;
> + fd->nmask = (BIT(nwidth) - 1) << nshift;
> + fd->flags = clk_divider_flags;
> + fd->lock = lock;
> + fd->hw.init = &init;
> +
> + clk = clk_register(dev, &fd->hw);
> + if (IS_ERR(clk))
> + kfree(fd);
> +
> + return clk;
> +}
> +EXPORT_SYMBOL_GPL(clk_register_fractional_divider);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 4080943..d76188a 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -422,6 +422,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
> const char *parent_name, unsigned long flags,
> unsigned int mult, unsigned int div);
>
> +/**
> + * struct clk_fractional_divider - adjustable fractional divider clock
> + *
> + * @hw: handle between common and hardware-specific interfaces
> + * @reg: register containing the divider
> + * @mshift: shift to the numerator bit field
> + * @mwidth: width of the numerator bit field
> + * @nshift: shift to the denominator bit field
> + * @nwidth: width of the denominator bit field
> + * @lock: register lock
> + *
> + * Clock with adjustable fractional divider affecting its output frequency.
> + */
> +
> +struct clk_fractional_divider {
> + struct clk_hw hw;
> + void __iomem *reg;
> + u8 mshift;
> + u32 mmask;
> + u8 nshift;
> + u32 nmask;
> + u8 flags;
> + spinlock_t *lock;
> +};
> +
> +extern const struct clk_ops clk_fractional_divider_ops;
> +struct clk *clk_register_fractional_divider(struct device *dev,
> + const char *name, const char *parent_name, unsigned long flags,
> + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
> + u8 clk_divider_flags, spinlock_t *lock);
> +
> /***
> * struct clk_composite - aggregate clock of mux, divider and gate clocks
> *
> --
> 2.0.0.rc2
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/